From 63ae952608ae023ef4915f62d029e7b81b788ce1 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Tue, 25 Sep 2018 12:00:29 -0400 Subject: [PATCH 001/235] Adding Guilhem's GST qubit map. -MW --- QGL/GSTTools.py | 25 +++++++++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/QGL/GSTTools.py b/QGL/GSTTools.py index fda0a579..4b8f71c2 100644 --- a/QGL/GSTTools.py +++ b/QGL/GSTTools.py @@ -57,6 +57,31 @@ def gst_map_1Q(gst_list, qubit, qgl_map=gst_gate_map, append_meas=True): elif isinstance(item, list): yield list(gst_map_1Q(item, qubit, qgl_map=qgl_map, append_meas=append_meas)) +def gst_map_2Q(gst_list, qubits, qgl_map=None, append_meas=False): + """ + Helper function that takes an arbitrarily nested list of pygsti gatestrings + and converts them into QGL sequences, keeping the same nesting of lists. + + Inputs: + gst_list: GateString to convert, or possibly nested list of pyGSTi GateStrings. + qubit: QGL qubit to apply the sequence to + qgl_map: Dictionary that maps between pyGSTi "Gx" string to QGL pulse + append_meas: Append a measurement to each sequence. + Returns: + QGL sequences, preserving the input list nesting (as a generator) + """ + if isinstance(gst_list, GateString): + gst_list = [gst_list] + for item in gst_list: + if isinstance(item, GateString): + mapped = map(lambda x: qgl_map[x], item.tup) + if append_meas: + yield list(chain(mapped, [reduce(lambda x,y: x*y, map(MEAS, qubits))])) + else: + yield list(mapped) + elif isinstance(item, list): + yield list(gst_map_2Q(item, qubit, qgl_map=qgl_map, append_meas=append_meas)) + def create_gst_sequence_from_pygsti(gst_list, qubit, gate_map=gst_gate_map): """ Returns list of QGL sequences from a pyGSTi GateString list. See gst_map_1Q. The return value is a list of sequences that can be complied by QGL. From 02b9e3ce4f4499fa4213ce7c826ee54ed425063a Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Tue, 25 Sep 2018 16:15:15 -0400 Subject: [PATCH 002/235] Fix embarrassing typo --- QGL/GSTTools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/GSTTools.py b/QGL/GSTTools.py index 4b8f71c2..b0bf2b75 100644 --- a/QGL/GSTTools.py +++ b/QGL/GSTTools.py @@ -28,8 +28,8 @@ from random import choices #Default mapping from pyGSTi naming convention to QGL gates. -gst_gate_map = {"Gx": X, - "Gy": Y, +gst_gate_map = {"Gx": X90, + "Gy": Y90, "Gi": Id} def gst_map_1Q(gst_list, qubit, qgl_map=gst_gate_map, append_meas=True): From f7e9806e801e6bf6e3e213171b853c0a70f26081 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 27 Sep 2018 12:08:24 -0400 Subject: [PATCH 003/235] Adding a test skeleton for the gate maps. Things are currently failing. --- tests/test_Sequences.py | 25 ++++++++++++++++++ .../awg/TestAPS2/GST/listOfExperiments.npy | Bin 0 -> 33634 bytes .../awg/TestAPS2/GST2Q/listOfExperiments.npy | Bin 0 -> 138231 bytes 3 files changed, 25 insertions(+) create mode 100644 tests/test_data/awg/TestAPS2/GST/listOfExperiments.npy create mode 100644 tests/test_data/awg/TestAPS2/GST2Q/listOfExperiments.npy diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index e4f63ddb..fe0abe7b 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -372,6 +372,31 @@ def test_RB_SimultaneousRB_AC(self): SimultaneousRB_AC((self.q1, self.q2), (seqs1, seqs2)) self.compare_sequences('RB') + def test_1Q_GST(self): + self.set_awg_dir() + # list of GST gate strings + listOfExperiments = list(np.load('GST/listOfExperiments.npy')) + exps = list(GSTTools.gst_map_1Q(listOfExperiments, self.q1)) + filenames = compile_to_hardware(seqs, 'GST/GST') + #self.compare_sequences('GST') + + def test_2Q_GST(self): + self.set_awg_dir() + def gst_2Qgate_map(q1, q2): + return {"Gxi": X90(q1)*Id(q2), + "Gyi": Y90(q1)*Id(q2), + "Gii": Id(q1)*Id(q2), + "Gix": Id(q1)*X90(q2), + "Giy": Id(q1)*Y90(q2), + "Gcnot": CNOT_CR(q2,q1)} + + # list of GST gate strings + listOfExperiments = list(np.load('GST2Q/listOfExperiments.npy')) + seqs = list(gst_map_2Q(listOfExperiments, (self.q1, self.q2), qgl_map = gst_2Qgate_map(q1, q2), append_meas=True)) + + filenames = compile_to_hardware(seqs, 'GST2Q/GST2Q') + #self.compare_sequences('GST2Q') + class APS2Helper(AWGTestHelper): def setUp(self): diff --git a/tests/test_data/awg/TestAPS2/GST/listOfExperiments.npy b/tests/test_data/awg/TestAPS2/GST/listOfExperiments.npy new file mode 100644 index 0000000000000000000000000000000000000000..7e552e2da96bdfd3a60fc76085f0e89c7e42faef GIT binary patch literal 33634 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

HrFS)ejNH3i%p>Q&GLPJQQvW{c zKZw<1cW-Xs-T=A-|PytnUx#&!YSS z|1 zE5NkGnVBNq7u3bQ1>SLt{CV_XaFQqTj`QIEPpJH$3K%GaprDzezYHwY`Jf61lzewC zIVcjL=)DdmL!O3<3RMhLaRWs;D8UeN5P@h)E-A_dP)ZpTGyf3;EK1Y9H1wUDmU zNErRWR>@UEL>dZdm@n}!J(lqw&JbGH`?bk(KrljxkswAf5fSK+d^FeNz%Hn#z8@pS zV^KVgi~ZkxjMvA2kLUWN*XN zr4kt1=^BDZHA{#YAZDf#7}V)og-8N z)La8aO?@Ln{@{cM3N*g+M41m|fkD9_FJvdaLOjlkgvth$W1ukDxkie{d$CYUKrJ;; z81$RWl&h8rwH(w61BJm~si%1SR|$19sMQ7vA-Kg%(O-2IY7MBh0VUt!N^jp5qmuUT z>x5blYJ-8I9Nf-Om;>3rZxrPYD4Prla}|} z%RnIqcbh3!?G$PksNDt%Ik-npaSrwfwHMUA1`0XYXQo`WU#J714*Hb;ou_2rK4!2( z#cG6I%=`C4A{~ZwL?clI?q??)x}^R4Q9+IYIj%vFfD_DUR);5plOmmh^ngY}03NhS zl!1ptdKl6p8VTe7s8ymrFD=sJke<*;82%@X5+~p(k)DS1j7GxfKWmj-^_)o0LwdoN zBEQE~LhvGM7(Bes(H+7|LcI*?6$3>nc$Fbc1QCdK2(O9qI+QmI3S#gkJMk6b47?@O z+o0YtP{_f%Mv6l4o>1?D`oKUT2p^g$`h(O$eFW-b1BE1<(Ni3QPlWmu)L8?CD12t7 z=#Nti^#!Oeeaio(rILZKm8%bvX7fbwwMgGU`c@-R1ioWOhAwm{1>Xzu1CSpz z2omrUGn&=m5d19CFOYuKNC?1hHi!xOJbEDY8(;oh<^2rU|3u0UsX!p{x2qC>f~iu{0YM>= z&WBW3BT)c~uoDe{91j!~q!^Im8Uz6-!Avv&NrwX^MY;e|DUE~xl(tC}fHESLg;Y)> zApjRzC0CUf=^{uKG!g<((I{~MDv4AXQWcGa09jgb z1YA=shgdYri*r?T;aY&ZRLdb7Eo~gfqm^*2!L`wH2uRz!I9Ih3?lN$f2VD4t9ZlZt z>CSA`ftKm&3R?0Pb{)N2kuU7r@C&;u=@I`_cqh7Jh)_di?Qr{2t{bTjW zPrAOIVlzPM2cmvZtR8{6%1}>#&PwX9LH)I{dL-mJO+5!=u+$Gh{m@uFA~DQRPrnLT z>PMh{WKe&a-#?r{7!|FjSy{$>wA7D5{aCK2@9TmwAIHVvSAG7NkC(~}RAweu(wI-s zRMMDFl*&n{oXnNpmxMCrQ@EJN++Sy&Ii^beG}Jq>dW^Z!*VC9!m-;N!&xqAy%x46aNx{VdeGv3iX84Vro$^Vw2A2la_qJ;r>lp`LzmvDD8){d~VZ@+BeOM~wXfcCdT* z&j!ozFBEDKsB8m8mxkmph3 z>;xYif*mt|{CrfX$3Q)9plD`!f+2r!qBF~rqC5rVX@f#D%QJd~Qu3@Q&p~ProV*x8`M7r3TgV+Owk__7b+j^L+B4x(GsUvW@OCDm>tIAB@fud=p3qyXgqDMPj zMd=2myFsCOrH5Xjl=Kv(7nI%x1ySk4PJFX?R_QBLKT!P*6e5#uq$o85gc=BHkby#U zt};_}&P6D%Md%Mv87M^PIz7cH8Z6WhP(uwAqBP7*(U};bMt~X_Q1Vs2q-j))N;=vZ zEz}rLV+|CgX&gggn!@7?HC~hqD47O@(lkM@P?{!+G6~9LgMu_oVJA#exHjJ0GgYW* zpd15*G$|uRX__un7N{8p3Tc{Yrs!;rP_sa}1`27qK~Hg-W(ze3RKh?ZO>@l@o#PQ| z9;o>~+M3MXPNR1?d)%ZiTc)BOydvPYDiP<9y<%F=GVLP5Gmls!=P8We=-UUuS}#Yx&H)P7J03>4CI&`42~?i1<| zsKW*daXMnA=xmTsM?oDkP{`A9J;h-e$I4ksgEexJIHNJ;9C)UFgv6-oDAG%iUe-uR(JMBIg7m6LuR(fUBOyd@SS30yB+^@u-quLS&^tznWAv^_??HNB zBOyW`SS30uB+_X}9|aO$;2_k%(9+#-k1o<4u7aD|8 z^raD@6n!Pg*Fe6}AV|@-%tTWZt_#ixiS#|BA2bqD^rKCp6#XR9&yar6NJ!DIR*B9B ziS#?9KQt0j^runc6#XUA-;n;%NJ!DYR*B9AiIlG>&IZvEXM=*PBqT@Y(nIuVIQ3!# z`AmNvJta6L1TMdpqc9aNAW5xTp4g>wHyLgE-#MG6$w`!+(lXrDXU=PIA|4xs|2pHmP6F4 zOsF(kdgp(8MxohZTXHY}?Jaq}(>zCD|n+a8)mSI`V9MwQm=BkEL z)(B;dqh<8knmQluY)n!~5T~ki>0h6zE)ag|?QqvZK^qiP}!lT1s!O>a4GI zRTru4irQ|`T8e6}>YiH50qr5RJyF{$T1&aiRlW7KuIeMTeNo$wYw401FGV@3KP~+X z;eg4DXI+&}_xc$cKsOU=AT23Fewp`|69#di_ZCZ`7lx~(_G;8#lUz%`)Bak0E&Z)1 zsU3{kAzbTS;)B%VGKHaBUNZjD-m4E2VK{^lECc|ZZXLQ%w#NiSc85`L z)mk&CRnDCDYLw4~YApL%HUgY@pE2mA^59=e%OduZt$qs9pDufy+U#Ggb2 z@v?NUIQ!u2H#qc*g$@`U`peql+z027!NG)ln5EJ@At$Yow^Bz0yC2w514hTp$Cygp zI12l5aZbQFX>jO>`IO0_;6EVFgK!=)IB0-}SxVhF?tw=HdlcAX1`NIMxCNtk>IuP~ z1oo5xLt8v;!{{${3-&Cq=L{G+keAINN^;@)wZ5QxU}aA0y(be+BsuNIn`n zFIsXroR^PWbq?Jx;grczol6h+;XIFS(x2OAAuUFD`+)q6_@g6;7q9~26ogaA;Ltwc ze52#4!r~NxQ`F!f^2J!fG)XB5cWrdbTdCrLl>k=KfYCnS0;WX#NqhyL`oI4$9{GC1g$)+~jQ zimZ{>FKq;C3#^?1qkg%Jsnm_5ez{zn_Ha5F9O{=VOb+!+M{%x%)5+kVUpljtx^dhu zT?Fe2teXKtzjU`?^iK5u12wFa1pz_e;891Aq-QVCa`Y zHq2F533fFw?^A@AcK_GQ0-tZ@W2fsx7z|-Zs^Im?P&UHBix^%qhY2$r z%m@ubtuoTa&>!FyW;B>F8U~#*mZ5UN1dzNw-YSg~YdowBjYW-;$&z-tW5(SZESA4~tIH zh&3D59F2t@NmwjyleuEu2y33kLW|7L!*bOEu@=Hw#1>!m==DgB%4P-UkGv}$xjS;C zG8dJLy!Ws0*VmaLz9Q%bY z?foeC3MpTS@>N{!U#^LIG{em-pur;oj}?0xhGPK=EHocln!e z8{JH(b+k<01o~vLp6*QD1d9CzDZd@%8@W8X2}u1NEW|c}^S()t%|N!KB8dA|6GCIL zO^`c*Y)?gy^BqQn&glqpH;|pF2m-!Ki*U4e3vv&TJ*fziz1N7)pVAg&ACUcwME)+3 zmycX^fDu}HBTK0avVD;5@<;YQx|vXiXo({wxq1~5quKv3Jro=%MG-ILN5r`w&QXIy z)BiD}^TF5rg+|h(L42mU@roD$$+6bUbbO$ z4o9$8fxTwH&?K*$Fz%B#1bY+MTLuix^0p0g)jNW{3+z3{CdkFL+%NC5LrdZ~}Q!TH?apkKaV zDU4KPjl7lmQn0UpeQm&~U%p`~?3a+De)(3M@8EoIaHwB?FgesOKZ^4coSzL2`sEjv zQa6tK&b5#Ms3IZ$SWB&ESUbE1dB1Xa}M2^B$VG)WzD4HsG zy;6*gaH}GQm(1eAlmJsw!%(YSU}IcWN|@4M%4isLN?C?*{Eas@vPa<4R^`OH5LS7O zMU8S1OWG}?Ua25VMKF~#4E0H63q!3^MVO1hRMjwOlWGiUw~RZbx>z+})znz%l3I3) z2ClYPbzr4wEHp{oJS;jdBvw6G^)(iHq=CiaHfbnUBUp_!7Fwi99+s<`iq#BObG9Ov zNAq5#1yj`HIA0Z>yjPcs&=Nu`7Sdvb*D0+TLAPPN{6jgfP1=al7EU{ZL#=X|(Q(z~ z;>UF}mfg(iE|#!kVVBs8Jl2v|C2K zqJ)_aCQHLmpUki@)G9NDxgN|c4TCmu8PaYUcghW7&4x8cW1&kDc8lJnxnkW2Yo5kJ zlg!VchN4-DtdF-! z8^pRD)<%s*U2+FY+AX6l*(A(nFk3VXb;(u>LtU~>m^;C2*D&ak9Smu=jJxD6vF?Vo zQ)8h^cG)d@mv)PF53D^J3th4|4~xz;iM0>bevO4LIbgB4OAd;4AFM+f3te(J56e|Y z#JV5W(ZC9>;O9O$#!@s1k&hVaxG*QcoYXO1vz%fmI)o9(3+Dr3JqYU|twkO4a2}Sc z9uezNSdVEfw9VtJT@)T&(#p{Pkb387=1dz(?emOS&%%06Yf%F| zZ?~w2UJ&a=STAWUbkWPKnKqOg=@kKA1^Aj4L@T|XCrIzy8v?!w@GUKfetJ7^kWNMk z_%6Wrv>=-5eLKi~^?`sN0z9n+(ODnm4Z7-M0nY&ZghBd&h4Z|WWoD{R>7HzLmX_)2 zGg?mY7CGv3TGB}h{+Wf$%(}yxsxRm+|HQ_D4Wxb%;a3R1u@C|1 z%ah-^Ji6x8BY#NkpQ!ySrIx-q`8&0idg33c{TH?Wacy+X(GdAc;N6a{IXA&M^nm}K zpG!CC4~EkcXO`qmM+f{*E6_WiUxWe>3Z@E(d?CF+e;!eBK$rf9Z$z#t!{vPSIL(`!+pcob#wyF_-R^!LS0vcOCZ$K35a!lHe#E?$!;J_LokhW46@zW#Lzp^M3|;v zn&}t>yt$EaRSRJ*1=CW;Amy#J3AJ0s(&%Y?ZcOnYYNrv}2n=Bf@X zaN6nkn$v!T)OJMem0asx`5zX0Y46149Fd^h3t49ox$?B4>2+HjtmuM7?|NY25BE*Wav+K3o{DLXdQ#JkI^!m_OZf@12bO7 zAnh4OhW>oFFcZK`^qI)dM}*1EQj?hSr#8w-ko?KwOo21i;&>e}jU|7263NS+BbWj< z-G)&!WEnA6%@AxRu0S*uJUH`mP~u7-GvO+=I3Y7{z_7IC#gh zUdlJ1{B|z)f4U&7_d07M3+T~M@N#>HAe(?}HXzhdTl9#lwhFQh$ejiRowS`9BtNS2){f%Iq~$xn5Jq0Ul|G35_Gg(v8X$HjR9&XX3$YmTQ_@&`{OFOW|Q_6)FRZ5TDl zb4JWn&kObfuorC@n&l0&xA{K{O!X~26O4>v^{Ul;3h^1{J`mBse? zdM1$it3GpKhNBxwE!IG+hOio$ENZXD7K=KpiC9fxH8WZ0uI8+1HqPvYy+^Z2}EzT%LAnIT>@SXu)PUHYjv=I++9})*b(5BCJ>#~DG$(9odxUy zuq%W7;}>CT<*IH>;j@$XGaTGh-KD$-%6oFT|ML@Jz1LH{SilZ26uiLp7Nif5z6OMv zsh=KkRewR!febJp=%Rtl(2{?ig!d_YEI5en^(Ts}=w?D)O-t-n@MAHuP1HW#4`fWEdqnK_=1!ND~bbx?_@7;=Y(H(iBKj z4HDX7no)9DnNYK7iNVvXR0g%l9C}>FPgcwOv8{w)bAjDx!_Y4Cn8RKIZ+LJN#vLtbJtvPfvp>kF;I{S3|tTCZefswTW~>OvJSiZ?lPLu5~7n`)j?38zA0p6VYTF zZKA905OEX4&4DOCt`h09Ev$uo7JLp+TgBN1=T3{`_1SioXvx_NByUf&gYNaScNg7E zsJm&2>}giY>$9Eocr<&0xoVeSyMf(f!_a4YmPiQH$8i}(b@Cv777>?xb*s;5PK2I8~6 z=>MQhq{p6PiI)DXEc*@hJl*T3tekD>rIn|278M&e5k-XE}KZVyeZ&q0p9`mt_h_6dXG8nhEj{YFV+XJ zJ~Ua>UZ*Vw62x-SvZjKLY&81fsKk&I5GSF9QAw@HZcf{B%sD!G32DJ24DG zaIpA?IDf+V%i?$)_BTtk41ZoHJXrjP?)6*sU%Hu4|Irc?`|xw=GPqIRB3vnNCzg+X zb9gYZM_0=`SUiUw^RaX3CY@uWr4d7q{f{{`MKXs5%P(R9hy`sTHCZ9{Qn!^l?0mrr z11n;~sLP6)Flw=4f)xi=!iJ&EN-~$at=wZ5h*%0@X`6^XD`OF<$;ygY4&sG25sg;f zCerCO5i3BfXcN(Cl}sYHS!EHcK)l!{qSdO}L|0W4u{y*WzUcp4P8er8+s2ZlMP_(a zOPJbV>KGWW#nKp(p%uv6Pt+Cb5?J+27Ij#Co8_toVl{--$Yi0x8nYIh8KbC$A3N!m zw{lGcYznZM38emN&YX5bsl{4|bt$ZtCX3pumBpeCYb{nASZz%fx~m;)+70CfyG+2# z0k$`RXs!-+klwi~1nda#N)w3Q>XZjaC)fn+00kyJ7_YeoG8D~R0D1WxB-T~1t~OcJT-Vqv zS6wUCb+87TEHu{;)}on%{46#kWSVKr~pnkfoQHQ z3&_niL%^8;uQ!2cu3342u5tyu0pRQa41NkI(q40zi-slg(Mcu5nhWbjqviG3Jl3KE z9MQZa&lhk3z=dXz8f{S?psTV4%mJ8d2GMPcnN0nGnc!&FTg@e+E`@rNS*4a+#-?ez zsq2;txB}oxGe~{6$_`TN-7Mg0fVY@IG~caEnzoz!Z;hyHq26Xz(ShsoRO#JZFX{%U zx0_Y8;l{jGI-Mu#Ca9auDtd8?UFCM%D(W_E~s}0>apPGhbSaF z>CSAmiY5R?-}##JYU zIR)kc9fM>%sAV`14+--ym`8LBqVTAZan)nOJPziGfH@r*B#iZwQHG8LqGSD(Fi(Sd zM#s=tKg$S@b%@YdKPSTT5MIy;80#0=;IR%39_yEcc^S+rItF9?s)?boeodIy!Mvek zFxGDx8CSg}%-dkz(J>h7ceMW*k>YF;m7*>40G5I z;{#Rluzw=rrx4HDL>m6j*z?h}3HV&FFMxe%!;plpm`hzf4#U?XegpAan}|?+ zXAvnE-;4MI#2;-U^6`^Rbk)xy{sQq=n~13VW)e9qzl-<>#6N8!QuCKhbk*M?{sZyf zK$PofW5M|^BGO>UK=a4)e5JyD!#Q*_q0XfxPNwNZ3eI`-c(9{M@+dg}6D&Wl0yYf6 zDac%Kd?~BP!6_u-`49`+L`RZ_$YAeOR;2u^8}$iXQi zVp)jgY$Aelp-psEc@ZyySRoMQy2My;D#pZce&^s+60tJGDmIaVb1{3t2X*hVN#1c$ zaHhTzm-E(}h{ad2viSPNopn~31lv4|9$G!g4Uyu>CVIQ49ztLlr`0AfR% zh~P9bi5#59A~u28)FvW0&1|Bpnv2*1;-$XmU!oWbO-t5-4?3DXRVx8o18idgDKl-E z3kR7$EQYaZC)Q=KE;m_-OncU}E61tnAm9}MJDNZw=1MzAq3I-GXMkNyAOh1h571TJ z1nds5hY3VpdRjn^OfLa@1MFi05tqJsfUfE%V1Iz=KFHVa#R4;cJQUtqzA{h)6>r4Kql{#&DBFsTd*BNJyg$ z5<)TBD7k8kNMj+5Ge}6oc&)@a$Pg(L(gcHq7)&%uu9_s$WJps2Nv;fwrC=&^=yR-2 zFo3+TnI@tGQQ1UF!*uriVT>H~P$04dn*nU54MQrfXD)U1I2f};bRphg6OoSD7Lfun zN5llgxi%3gxzQ%NYMzMmAuh0qNXtT#$bnfTVm8Dan~2op+C*0^7I6v0rGY4yI>mx> zQ%nqx0ysF!L|hJWg-xX3tYj|?PUul^Rta`9u+=sU!MTOGFgPK{!MRn$H4xX@L5(A_r%Sh+83Uvxx}Koi@=`+eO>~@h)HV zuLO#P=5E$-9Dr4dOc0!#odWIxxZ4C$X6|7w9OT$R${w-y!n)UFAu{_|)2K+_fe0D_&CHTY$Cf0ydDSMm>NOExhxmp~M0DOXiJY6aM0^|K zJ2ny7dDkYo>OB$Phxman`j>sgLh~VO!AA}n3pvBVQ_X1sKLYr%38c)NVNM1)!clBK z5$jV}XH6C&^BHT}mE+WWF5njczchhJ%vW}hLi4qN-vIp91R^lsM+;53qmuv`EJU(m-g zb0L$dU)@GOO_vw-BB&M2Dg~z^o6(_;9oSS7urk0ZW)SJQm`T%Wa)7FeS`BJ-vx*4S z$Wx^h)fBZB)Y@hhIjWPl>Z&wR>q5Q6tRhVH>?-G}zNihLHZ-e9RHM9AS2Y&33Dl;6 z8h*n}^FcGZGg~#MWx8rX%L(3szhS=AyA}C{*^PX|+>##ir;%24Gof13(*K587`hOm z4I|+<%#6@i+igW?2jMcEK)JkJE6}R97oh`$D|7;a*pUq!dU^ZfU@hqEle$uvPGCCg z7$mQYiJ`!C6{Z`Q?m7lh>tSSE)l-;WV0!BqWUG&s;YjrrrXQI8ItF1%H!`joAk088 zg97F>f)Yu~RZ)ia5z(|dP5nMF4f z%B3Z~tSsXFb-?1@LPQGC4fMFoSvXh@Gc;SUIlvM&jIuP>gi)Ms6l@-_`8Eu(TEHAF zW82EfT1XH0+q#HuCR8>pjoXTZ<6kT3 z2?4vyBvHEV7HKD>T?Prk+HI6vb&p7UAni3s2-Lk=iPN-Cr2UW%7$gMgpiy$weIgx# zbU2XYGKpA}jxa|{{};amDd10W_tOJ@Hy)*%33ZH?=tiALkvdL~2Xh$>Ui7#{oe=CK zuv0dSqV<3YqjWte*h9b`wqc0aBh1k4*2d&VZZ>RAz=gZR8nMD$)TiJZO{MSKb3%Qg`ae8ncZ>Qxb6gZO$N z%2gb(6uuD?!{ZlD;hQ491@Ud0NGW`WJ%2L6FTV=sws!@457_%Qj8gc438NH#DA;LW zAK5UZ@MGp^$teuCl~Z_z9`IB63EfPnPicu1>fSx1@GLzen!>0^Df~>t&mn$c6OqC% zZKA8b67g$@-`GT?@LQ9}Df~{v?;-wR6OqCnZKA7w67gq{3qoqI9B+rC@(F6V<|4la&>K|HqgPgK*l)r!JvFPwdk2ooU|B02a zjAxxgORa_YolB3Brrl6Z;Cb{6f93uMFuw^z`U==V3SdD23jsXe1R{Ke^8j5{M8Ki| zi~Z_B)|(yAfi_)571Sm1uO%wtPk?#C9&|8V-M4ncX>(l)5L`$m4|eZ zL88=EU?;r1B%Jdq3Q`G3WdlNStD;3HXcr4o6-YG$f}B-nCbmtSuo@!OgjCBQAz-yl z5=EM z2(Y0xj8Ztvgi#8I3pN7SNE?O}j$$rMVPxIBGqs~d90PHzO+*UESwu?Vco8!oX4*ug zaDq*wGjbwMf;ibGB85{-BByYwh|?fCHW4XQHj&QLiI@d(hA;Y8&%_cqlQkUXV3i_M z7l-hA0cQbpO&|sE2Ij&+jvj;2AkP+S4y=U9qWsOZSQNn<#hM3ezR5!T7OP@V@D z3b+Vhwh2V~a_k@lFjv6E0GF6Rgl}mcAf2HTa2demCJ@)+fJXry zGl3Mqxvh39(MXI%Tpbe-BtJir|A{Jp}7vlZE&_!kTtNIf0J~_!z*)O(4?u zgdL;+J}KZ+0G~F22;Vb#fOKY0z~=xyZvv6M7c3yh??nM$0{F5CMD$+C1ElkM0=@?D z^#J6naAN6uBNYq}jW~U83iuYlw@o0W?;Yl%=?ghZ-@9VH2kU*4Md|y%Vo~}&6zeps zk4zTQ_c3eH^hMUpI|e)>;3oh-HGxRqSvyGS`%J*k0e)cuk-jhU0O^dLfL{ar#sngL z-&#OU-**Cj5AX*Qi1huK2T14h1pFD`F98@_ZWPPkuS}+1#}oZL{F|u1L;b_7QV9QK zGdk3?)Iw0tH>(I{;k;Ek+b3#KsKv}G(plWDazIOnS`z97W)%@FmA6Xg z{X{JTwQQiC4t_%hy-(%n&TMrdEz?zbT2Am5-Y*oq$otp-t?2lR9kcK!Yh6`=9`MJj zBHc`=O0+}@PNSFwd^ud14IC#%3@@juiZB<0sj6cr=+zhruW8A|iqKS6U4$ACYU%_^ zb}g+yfvzn=9SCVU0a32YMr>0!!IubA4@`X>gX}giG4zf!6s8fF#ySRJZDM3x)l`^f zV4CX~q_c&V;ZR;GOiM7WbPVFy+Q_)7jWBJ&wDXzBH7!ZGyDWE11K+8oPS>b)B4Kq_CriS3>M$6Df_I+2b5USC0bOMX;{Gy4f%aW_J@t+3X=$ zPhh=l7&6+MxzugtnD!B|FT{Q}5pnHr5h=0hA`XB!&?X|egKVO!t`hNTh}YOeg!o#M z$T_}F#K91U*hJ)cs7-X$FcF7C91)1}wP;eLN3s`uT#n3yt{NrSXkcS(7$QBEx!~|K z!XeV*L>vz>!zNOsGucaBJ&N=M!6pKmWWy-ZlT8>!dWv9Eflaevh_u68>b7#Em59?J zX4ynUdWJ=$NY51UdWf@ZA|mbDL|5G);%ta>Y$76^Fo_)Lxgy>Oah^>?r03g2S1k~6 zA;d+#=zj^E6zA-i6`ciXlFkt;7uI5vg)}c=EjW(V%+X5)yb0hk6G&lR&RiIn=*m%^ zSBSL|)+&=lS-#m~QJhzcbqlOpO%|fOhBfVma+=o)cpJcVCJ;$pZwD#N8w9)^;6@XO zAm5P(=&DTuZU(r;1R}>!3pvWQBsD<~sg#UKp?#BQ;$8w@(6OF+6q?8Nqa<~+N5W_Ra#)_<0j zWq-c!%sDf&vvco^pg0oZC{IMDM|)xv#!ws!ahxY2)Z;Bt6Fq_AM2MexB2qoc6QeMh z;uMHeGh*iJro1>$E3lH=KiN1>r!@oCOv^%=XQ_s57N&>utjPoD*#zeR{M-U1%yX5? z1tvK-$@4r~^I?5qS(4=ijwNwkNNW+S#g>IAFHtRZqMGKV1eXC^Zh=Vh3Llg(uOzq% z;A#s*kk^y|qp+6XI)Ljf5IO$R0X51S2z~|dYYRk-zbOGm;ah^=0sKA->S^1&B>$jX zc26#AA#dCKBdwoc{cKrC@-M1o_rCcWHqE~h+z9YD3zQ`Pu3YNiB*}l!`V-b)mL*C4 z+p#3c|Iqpu)+RCuoNp;yXh`y=@@nFuy1a>MlK&_FB69*~GkIDOHn%_|c?%zuByUM@ zD}a?O5J|3F0*u1e1h)ZL#R8G!Z5>dPT$NxofZJIhl3cw67=`T#?f`JdESNnNnwRCB zioxWzPd3ao2-XBx%LWnXos}#;x0>8K??SaU)H=2*sjjQ0JmON(I6KKm`}#J|nFMf_*>oN|1Z#R%t=yM>nWLMB#6%F~L_N*)ob;drwO za#s4NbRh~yQ#b~~v1tODbDSDDq>jIZ&W=LD9*!q-0+giTQi;e6C|-+QUytDR|+>lxH(NgV!NqPFcnQ~MCKMS-P0H(_Ew9LB~n4=HZZrR zF-U9=lZnC|WbOoWR~m!F-kr*5VtbP51*UfzgT(eRnJDxn(+|u&87A>PO5V2i-XxaU z>1M-wAF=y^_4hCd@dG9ng#p9{0vqIE$n%5B;pjPggpr8!V2TexeAp8u+K;H0+3@IR zRCto^M~OWK>~Rm1ls{o%67(mDJq7G(4@2OeQLcEdn)zobJ_qr6Pek^IIHJV=1&S|1 ze9040gO@!~Zh}g2D8yGi5si4w615bsQ+xyBo1Tbryyb~ec$?xo5Z}#;{OQoVe!Qn1 zG9#-Zc_0wpC-wob4?Rr!G0eoGFr3&&z&`dc^y3rdvY#%KV~aQWpHdtFaik|oKSrrn zJXh(*Xkuf4jrB0;$2bd4{O8MR7L7Ii84qd~S)_kGT}*L7eZ2=*JhH7=;BC7eZW=75SxQUOyJAm&;7z zup=xXwiMVh50ic@H?b(JAhr_NDi1?HRx6jwOk!+tKh{uO3vrz%Nsq%8>6X^fssoWz~9`OmZ zYgxCl?)jezHZQOW4zISLwI!^rEK90U$+MzRnby{@wy`WUql#*>G?G8;oBX`6Ey1b) zt688FV>{(?3oSWO=|**0+r!$yvZNO~I+j#pCt5XN)wC?sqLyl@6V+zyOmG)~wJi{> zsN;iDjJgEt0jzI`8DhfO}gYD$%e67==az_W`(X z1}x`Kedg^o_EQpDQjCcoR>S_p8Us7P!=xGqnphMLBGv?0Qx8Ko4pt6fE*M+9*JwuZ z5Qv9*qLkw>^@`^z&1g>Sa9~Gxn6#sXg-JD95<3!DD-T0Gj#938uG)>GDINpySWiSh zj&np=ddE{d0pf|Ch=QEtiBUM2;wcbM^+Yt}G)vTaoKEo!h-Z2tDsq-5MxiyuHW1Iw zioDt&uOH{Am&;7z$R)HTb}q2eVXUIMYb zCrUpqRj+ui(vQoCT@I{+he0L*$6U5G*h<;q}iE`gmiUHz{o``;Qu|(}hSBf`5yx9}ck8YkQcTS~v3&idjF>~oc zUN>$nu#yKGvIHw=-3IG+%aUsJ@GLpEN9#^lcUcyiakpwX*ubKaK5)ktm!l`aUI2Sr zpcJEza=Gv&Co0|OORFEOdn`+Oaj#=ZHSVKzKdk#TOca&ObIXw&k}qN;PY9q zf7!sY!TFyxhA4-yU=`$LFT6nOMOZIcmbBt!&yw?ew1&cZ)w0lv*Hp_Mg3I6vTk$%< zHvqnAfzpb%luMncwBl`A@4$N3vZNL7IhM5IeOe#D`p~k_ieajyPE=bloZv?QKej-$ z;u9Z~R(wiu1i+CNh*pd$0Y+go!7%{GS|D06&H=R*;|WdxIMD*piqA@bQJ6$}>X~hCs3t=s?ENR7J$C6eop|up&GRs0MmaCRJQEkNvf-3>8vOu(AwGYaY zTSIUyz;zagR;(`pM&V0>8vuS~foR3o4ydj8hTyjVzq3HJ;`_z zyk7iV3?`2*WEuWK@K=BvZBWYbTM1Ck?h*V0;GZ^#hWw>u$`w3`?*RX%`VZ89ZB=Tr zNiCe+ONKEyZ)wV=@-MO{_~faa;FHI^E0?$+r7N5Jpwwjxf?ERI$_7!GN=jPu*3ML> zx;4~oY!$7kQc{(bzAe?NP^;N0s0^t!b+$QLWNy z6n3V%3)I@G%8$Zs5uYsW(5Z8mP)Gi*XQ(TWZ9+YHERP?0h5GU+*R@oZf7cNIP_9}E zyUJf?^0b>gtq8lzqvQ$i!9!Wy-l2iMwMz1>@jhS=zI#u+d#|E*@6kK#o%(LMjRN1@ z2=CrU-`z0rKS$N}RiIkI|2`hUel+%n(O8WH(X4kkK!N=6OPdblyAQ&K4_9G9fv|T_rD#H$ z_bUi>1axH@gv4KEL$YMb2we@RJPktZuW=x`Z7HGa0Ch@(kp0dEr18I=&<%h>8iWGe z=s;2ELZ~aCn-nTrp6tNQYG?=K*gV^TZhUuyci*D#ZkT*~+=1>2Xk-Y)vAdN<1&rI$ z4C%n_mJx*>H12?LXPSWy+@*>}CT}G1VD2W=6Hu=-NIKA4nW70v2l~+H3!`6}Asx8K zFr)+b(zp-C{b>d|&|j6J326r&AT$8bz%&RQ7-U1TWF90m7|=s$5IXR%14ZExLXQG^ zEDb^j9ycKEz!QX?1oTuIgbqCIKv8&x(6fM^%Rq?>1?$OQp%Goe^YT=VZ8DobNe%lj zL|)1q;k+PEE5eKN7#}gGNpUY;QY*6oOImRnUnchoxS<{=6?xU+qVO8I*TKEvacIk% z3d#@BAs9F9^zXx--h{)XG(G2RWWVG(w+Cny$|jKkCP64Xme7dVdRE``^e)^ zr;im(o3S?P6PlmG9O0R0)=1Zsr8tV_XqaO>6D1q#n^71?b3Dumo{7Fqv`wwtXEZ0l zoa~vX-W1=A!c>~mU`|)F9QRJi^{#iAp%QjN@%>J+JE55bW&xP3K*Itd?%NzS(A|U) zC;4+SbHU6r80p!3i;2P)WEOx~XfWv4B8AX~{PDy?T1;yRtfht}y;`PH(afY&%gL+& zv(jLsPpb?@%Cwry8Zc`O20dD*P|?h^MC)mN32TF4p+8^QmMo>OX?+9hTf;(czH=yNC(Yjtx~=@R}_EE5Ka65H|@ zrN1Hl<4AF3{#7fpB9m4e(M{ysOlFU_sXUdNd&(pBcqs#me@WJ6^1AFjQ@JQ?PHqcu zTY4NSw3UL`U`x0XbCu`u5G&EF40CJGlqzkbV%iX;O;yNk3$CiiNu{dUoV02?a@E0Y z?{TQs4hp8tSlhKD&7EM@@Jv*!rfbSltVMHYn7eo;s#e=Kqfm!tU6}Pe6P2rPo7%cv zY3>Gdch5xi8u(@u_Mo{Z%)PQEuLMrCZ*TQ-?c=A6(2!CiNc%Wa+`fI)%4LyO9PRzc z?GLW8$4UDRa5%YtC%J>bHSsvKuc?B$Ear_b9^%0?o54K9Go^ips+cxoY2RVwnu9ys z_IwvWyU^04ckO&F3Kvnk7}_P4hT64PPj=18i6b#UjzS?j^}6+)Am6=a44=v3dmm-1)`P$u0Pa+vVSx}g?JhO2yG$5y$L=Q66HG6Ik&5-U zm?-oi(-%xXgF(0MQ3xB6{PDy?x|h~{uuq(={u z84TtjgF%fRR;XxZTA)X0JqqhF!$N-^w=G#pPtbZ2)>DRs;ymqGa(a%|v#_2sEHvhM z!_uw{q4ff+7Yz$ldC9S&@G`AeU=7V$ywX2c6*)JjSSAb-CAQ@?O0PqD!;#|3ys1`Z zMJBB{qHmFV8{9h{C)Ih^;iB*!x%a_+;BlzXhYDhEf(4(OtBfxm;xL-SVSeP9Ql*bo zOq;Q^=@W9Ff*av+QmK(PC#@PqZZxQCLoH1-O+ShxV;fFqg%=@x?=2 zO>+&*wVo;MTc={$jHP|+$$bfKgU3nxzOp%K-`C{60r#!Pp?%*em^Nc=-}f|sfcc|m zqJ2NPrYyyuY5oH9SIzh&7WS88B zpH1bdoc5DPTpD&~S?$n7msTDOCO#&o54J5t*T zS`AB+($#b|>0K>qJ44&W($Klu>KW73+SMUh7i2w)MCIz2B4r8gN^&=lyIUk0*PtXR zr}s$i334xsMB(;!N$p%il8r#_W0C0Fz9q>h>_>8ckc~5B;wEN^9o_*-Vuy#7!Ok5> z=^#i=94YQyQ?;=9&$r@CA55+pxI;Wnig&2PMd2`V&A}b+acJHV3Zf`QRMeb;D$9SA{?^v6Y;vGlscyK3p9Ljg1f@w3>=AA_IWSFOTCfawZ zYsykQjppev&+tst?@Zs6lY2BPqigzuw>!5YAG$~zYSCigdPwfV1!P3yV z8`U$WtF`MwvMb1&EE1KwxfCf&up7w;Q6T%D%V@NWaapwC-foI7f?R~iYs@IQppUGiW7Y=wfmsm zZ)sAw{=OE42dE8zHqg>gxk2hBGnhBJcz_R*91QXyimc8-NL234l4KO#BKbDRcd}&mN2d~{dso3^bn-rsh4-kv5A6d} zi<|eMddZcY=f&9`MshgFk8D!v_i;%w3ZIbt6yyk-LoZ5QR7^Iu#!ok({s+NFdU?c2pW)@PUJggH{H6SdC{+s-8PEoKB}05APboPjcE3p{ zWZ>VD_zuMPX#}$RgCd2q(S-g;=O;Knr#Z;xFQy}j{FTl|IKQPi$l~v|6NNwM{0Zl; zGzZ!HJKfR5{X^$pIGadUixx4mwyC_5vbJL<3jdQAGplMdc`Elj$U4gtYa6NABI(HS zNM35Tq_Y*AN@i0tuzOz+1YmF)}nN3!>Nf-Rvv-_#^ljj0^~?LbdMybe+?WxBduYC^Ip$b&r+No(ej61GD~9t!d>k3{I2 zyQJKtl;ja0TX-b$*U}_4f=7~U1@b75L==y9$tWB{@>r0^Wl3J0T#&}&6Qrz=__!`F zjVF*i5#&i8DQP@eL8LJslr)|~?Nn%|d77m0bW@Wwox@%F9_ z$+JP8>XTaT19o=fsPkmq|O(s+SOM&Uw|?Lc1Skx1jkCaGz>gk*b=mwF`9c$rJe zjY~;(0C`20 zVqf)gt6D$mXFOTe{fONI>|P6#9Ny<)62<$8^#}HVg&~FmluMth3{i$Rkm4YS4_YEp zIJkr;L41hf!w?^_M1=6sQeqSyqxd+)CoB;ee9{v&hEGv^8samShzLGgN{qsD6rYDU zBqQpDq6Hy*K|yT2;!890;`bt{mq5L2P?Ek^6vAhX1SBhVD3w>Cyk;m8yVuhd3EUf0 z-h}d&p&)N>t5Y~FP1-x8-UaoZK_O`Go0LTC15zJ?8fH*P*l?SQ!bhY&2K9+SAzGiN zQyQufq(*`oWl+e}Xq%FIlad+>YFw7$C7A`08m}I;Zl;zKrZ)4L~(^nMqwq%RUlVqNnWB@kj6CyBoBb} zcyBGqbs*P!q@?jn1#@X6C~4e4?JH5A_pFI+3{KX?BjlYuI2=X_NL>hm0NjY;z@=uU|c_h;Kw@GRm{~`G=$W7|#NxZ~8 zxfva#aZ~vhaxyPvS;nNC&y&})=kw&LoX^XUnO`hSPS5Enhhrd&DY+%nFm6e4D~OdW zQG!@mz1;8$kN37Fwhgc<7A85|*25%)tYeADU|mnt7}ld$AL6c-hzRahN|f`16dOR?BO@lR zVk=1Eo~j}_SQ!O-=)Fkp4YHv}N)#I@h;3p%D4U#psO<}FKTne^?r&-m#KzPPfOep# zA&dv9moi;VViS^0K_2XpNMkdPlqeoT@=%b6c_iZ4+$H7wAju;@w(v;gv873B7>^{` z3gl59i9jChl5&2K2uX2wxM`7#B(eWDQsIplpvl<@jQs zQoIUcnI%dJuU0RaLVA+Ia$?s2yVk-ah1YqQq_7jQ&cLp>Fr@GX<&r5(jt%DoDc%UN zizOn3T}z0P!kZ}G46&OfB85>YQO*cb><;l(OGFAQJW*448^zlp_OL{x@QzZVoDZaU z7sR`>V)ojff*kf#Q@&#^xb?Yg9eB||FS5PC_OV$BV_!v+E4=VXu^+{IAl_?>lF0i? zh!V>CDfWl>fGr}H1JpF9uG`0fWCwwL&}Na%!KGP==R;&42K$K3BA}1{2UgA&l6@TP z6E=&KK3Rg*pgu+RX|T`OETa1Ce_-XjA=&4_4#~31vlqF@qIyC8vS)Zv9@~VM zT#$#CUcpO4PF~3nZ_Cq)@Qyqd5%}!&t|B-! z>12#Qr#2VbJWrE2&$l)CRd>`DKwIc(NcJN2aIAqtl|;$na4#mg1mseWl#nk|P{Wls zUCH@!YAc|v^fbx)DpQlluco#J+FDOT^w+7EGF?sodXis)+~ARD!B-wBOYm!w-+=tq zBTJql=re zIms$~LAZO{qd{TWD214Na-0UiL#sV!GOt?MPM! zxxGiCDLZ(iEWsT~?gX-iN1`b;T`~%_NbU@B7mq|!YMZ1sr4GruAnSP~no{2-qp&N< z-9YZ1Av53n=e49k0hYIom8G}`u|0w9Wnog1ycuzf5H9obj8?1zQ49m;+b z_lMZn5~U&ssFw?2-dv?62NF97SQ87AhBWmsDapaangKh+!cdSyl}n$icH}UM%^@Cc ziRi}>B}7@dEhx5xc%&tw9<54=a_J)B1EbZBRInv~>BTa%xcrPdl+8&5+^&Q=epE*xFF zCpm{?Taf2^q}1d*1yiOg9XX%c1<)?^H0en@Q1sEMyF1g{|35#*H~iK<-Xl2Irlc{Rv#k3?6lF-a}TwIr_t*~ueOmd-92h3iS) z05W9A%y)`;ExEA(%iFrjQtU#kE3lg^OiFUIi$$Ruu?W~L7KV;=R}NcOEG;QU!AFx@ zDONzd%@U;|x2u;6VcuM&B|V7U0qjl-lZM>oVN#O2iS-25%fe8Q-pZxVRXfs$Vqb{; zED`;7*QIGzmMES{CiUS}Hv_!OHkSA(E9;7%J;zO2*ay(p0lzUxJ zd=%niSy9jI=k?=p^|D7FvL^C(Bu@}~64+A~CjEHY#pIXj5qlQca~6hvJg;2#=%a9K z@or=Y#TOvHXo=E~m()w2tMub#Vy^%jYGKlkS3OMn@fxw$fxTg2=*OGNrO#FS@fO9m zA--dY=*PPyL|J<8QG6fb2bPF_d{|11!Z3=%A%0|u=*P#NsQvha;-?TtSR(o{vXmHw zQ4~i*9FrBZ-#O$pWNfjRw_%m#IF90Yh!bp4YBI5uC}#vIPJ%eu7SWX{Y8HPvkUI|- z*JUc%X<(<@tQ2O3qRCL^O Date: Thu, 27 Sep 2018 15:32:47 -0400 Subject: [PATCH 004/235] Fixed unit test with Brian Donovan's help --- .travis.yml | 1 + QGL/GSTTools.py | 8 ++- tests/test_Sequences.py | 61 +++++++++++++++--- tests/test_data/awg/TestAPS1/.DS_Store | Bin 0 -> 8196 bytes tests/test_data/awg/TestAPS1/GST/.DS_Store | Bin 0 -> 10244 bytes .../test_data/awg/TestAPS1/GST/GST/.DS_Store | Bin 0 -> 6148 bytes tests/test_data/awg/TestAPS2/.DS_Store | Bin 0 -> 8196 bytes tests/test_data/awg/TestAPS2/GST/.DS_Store | Bin 0 -> 8196 bytes .../GST/{ => GST}/listOfExperiments.npy | Bin .../{ => GST}/GST2Q/listOfExperiments.npy | Bin 10 files changed, 59 insertions(+), 11 deletions(-) create mode 100644 tests/test_data/awg/TestAPS1/.DS_Store create mode 100644 tests/test_data/awg/TestAPS1/GST/.DS_Store create mode 100644 tests/test_data/awg/TestAPS1/GST/GST/.DS_Store create mode 100644 tests/test_data/awg/TestAPS2/.DS_Store create mode 100644 tests/test_data/awg/TestAPS2/GST/.DS_Store rename tests/test_data/awg/TestAPS2/GST/{ => GST}/listOfExperiments.npy (100%) rename tests/test_data/awg/TestAPS2/{ => GST}/GST2Q/listOfExperiments.npy (100%) diff --git a/.travis.yml b/.travis.yml index 6e781a45..c8206609 100644 --- a/.travis.yml +++ b/.travis.yml @@ -38,6 +38,7 @@ install: - source activate test-environment - conda install -c ecpy atom; - pip install watchdog coveralls + - pip install pygsti script: - coverage run -m unittest discover diff --git a/QGL/GSTTools.py b/QGL/GSTTools.py index b0bf2b75..0052b1c6 100644 --- a/QGL/GSTTools.py +++ b/QGL/GSTTools.py @@ -23,10 +23,16 @@ from .Cliffords import * from .BasicSequences.helpers import create_cal_seqs from .Compiler import compile_to_hardware -from pygsti.objects import GateString from itertools import chain from random import choices +PYGSTI_PRESENT = False +try: + from pygsti.objects import GateString + PYGSTI_PRESENT = True +except: + pass + #Default mapping from pyGSTi naming convention to QGL gates. gst_gate_map = {"Gx": X90, "Gy": Y90, diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index fe0abe7b..59c4fff8 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -3,6 +3,7 @@ import unittest, time, os, random, sys from QGL import * +from QGL import GSTTools import QGL from QGL.Channels import Edge, Measurement, LogicalChannel, LogicalMarkerChannel, PhysicalMarkerChannel, PhysicalQuadratureChannel @@ -373,15 +374,29 @@ def test_RB_SimultaneousRB_AC(self): self.compare_sequences('RB') def test_1Q_GST(self): - self.set_awg_dir() + self.set_awg_dir('GST') # list of GST gate strings - listOfExperiments = list(np.load('GST/listOfExperiments.npy')) - exps = list(GSTTools.gst_map_1Q(listOfExperiments, self.q1)) + if GSTTools.PYGSTI_PRESENT: + # generate the gate gatestrings + import pygsti + from pygsti.construction import std1Q_XYI + + #Create a data set + gs_target = std1Q_XYI.gs_target + fiducials = std1Q_XYI.fiducials + germs = std1Q_XYI.germs + maxLengths = [1,2,4] + + listOfExperiments = pygsti.construction.make_lsgst_experiment_list(gs_target.gates.keys(), fiducials, fiducials, germs, maxLengths) + else: + listOfExperiments = list(np.load('tests/test_data/awg/TestAPS2/GST/GST/listOfExperiments.npy')) + + seqs = list(GSTTools.gst_map_1Q(listOfExperiments, self.q1)) filenames = compile_to_hardware(seqs, 'GST/GST') - #self.compare_sequences('GST') + self.compare_sequences('GST') def test_2Q_GST(self): - self.set_awg_dir() + self.set_awg_dir('GST') def gst_2Qgate_map(q1, q2): return {"Gxi": X90(q1)*Id(q2), "Gyi": Y90(q1)*Id(q2), @@ -390,12 +405,31 @@ def gst_2Qgate_map(q1, q2): "Giy": Id(q1)*Y90(q2), "Gcnot": CNOT_CR(q2,q1)} - # list of GST gate strings - listOfExperiments = list(np.load('GST2Q/listOfExperiments.npy')) - seqs = list(gst_map_2Q(listOfExperiments, (self.q1, self.q2), qgl_map = gst_2Qgate_map(q1, q2), append_meas=True)) + if GSTTools.PYGSTI_PRESENT: + import pygsti + from pygsti.construction import std1Q_XYI, std2Q_XYICNOT + from itertools import product + from QGL.GSTTools import SingleQubitCliffordGST, gst_map_2Q + # note the use of the germs_lite! + gs = std2Q_XYICNOT + gs_target = std2Q_XYICNOT.gs_target.copy() + + prep_fiducials = std2Q_XYICNOT.prepStrs + effect_fiducials = std2Q_XYICNOT.effectStrs + gs_germs = std2Q_XYICNOT.germs_lite + #maxLengths = [1,2,4,8,16] + maxLengths = [1,2] + + print('Creating GST sequences...') + listOfExperiments = pygsti.construction.make_lsgst_experiment_list(gs_target.gates.keys(), prep_fiducials, effect_fiducials, gs_germs, maxLengths) + else: + # list of GST gate strings + listOfExperiments = list(np.load('tests/test_data/awg/TestAPS2/GST/GST2Q/listOfExperiments.npy')) - filenames = compile_to_hardware(seqs, 'GST2Q/GST2Q') - #self.compare_sequences('GST2Q') + seqs = list(GSTTools.gst_map_2Q(listOfExperiments, (self.q1, self.q2), qgl_map = gst_2Qgate_map(self.q1, self.q2), append_meas=True)) + + filenames = compile_to_hardware(seqs, 'GST/GST2Q') + self.compare_sequences('GST2Q') class APS2Helper(AWGTestHelper): @@ -713,6 +747,13 @@ def test_RB_SingleQubitRB(self): def test_RB_SimultaneousRB_AC(self): TestSequences.test_RB_SimultaneousRB_AC(self) + @unittest.skip("Tek5014 unused in years") + def test_1Q_GST(self): + TestSequences.test_1Q_GST(self) + + @unittest.skip("Tek5014 unused in years") + def test_2Q_GST(self): + TestSequences.test_2Q_GST(self) # class TestTek7000(unittest.TestCase, AWGTestHelper, TestSequences): # def setUp(self): diff --git a/tests/test_data/awg/TestAPS1/.DS_Store b/tests/test_data/awg/TestAPS1/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..d5cf7472eb5ae76255b7f9758306f85608b99e57 GIT binary patch literal 8196 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 literal 0 HcmV?d00001 diff --git a/tests/test_data/awg/TestAPS2/GST/.DS_Store b/tests/test_data/awg/TestAPS2/GST/.DS_Store new file mode 100644 index 0000000000000000000000000000000000000000..3369fa11e47e430935191879c3425e5589fb46f0 GIT binary patch literal 8196 zcmeI0O>h)N6oB8`{4g{8q=Uf>OMtD!6j1|Ylkg)F*lbpW5+t(u2MoJAV=`uTChp8; zLt?Dx0ZZiyC6;>dAZ2k9e@<%Yh2rEvQwF_Q9;~t~_29*;ucvz>A>^o1#dKBo>+bhn z|GfETdV2u?lSLyApaXzHnM<~oin}DP=Xphn$R4gl@`rdj=jJC@*o7To1i}b}5eOp? zMj(v9y%PaCvw2chIroJ&EW-$d5x6fSz`qYM%3LO-oEI|qbWjmo0Z3L-caW&gc!gw4 z%7m2jLXw6O3RjZC73CEJg*)}fq+LSFc_D>6puBvboEhZ}1%ugX{8(TPm=H27!w7^C zSQ!ES+El=kkcK{cY{mUuYVvL>Wfc7!z~VOx>Msrd_~cz<70u-MiqWdXJkSo$WJ}&; z)^@Wr(#`CoZFzoM+ag9PDyyp3D2k?O>$S7#akrHAGk(!aW(ucyt2g71XJx(5b?v2k zCOcr6rBPPb<2ruEa?C>DY#An*oEfwn!!0G@ZR& z@9|D{#_-40l zb7ODFH>Z7G8}ZzN-xC;)GFJ4=QzqG2Q)Y^+$#o+i>ExtuxsEqr7Ce$oi8irh_1d~k z(d|twJKOgi>|R*Ep@!9N)U*)_jpJL-S!SWXWEuYWaK_7-j$t`tgHx7gWo@(1$>h!2 z$PHBuydJFA^`T|3F;7RQrVmbut+A(M=l;hueTe>w z%F|Z7T5OfwU59l2Sw8dejOU-6G#y&iO*ws%RyE9Z-G;%}q%IrubizUC zg<&wD1Q+2FT!#1H8hi?$!43EtzJ>2#5pKax@GJZVzr!E!7gl05u0aKBaVu`a?bw1l zaW}SM91q}O?8YSa;Bh>Keb|o!IErIv;qy3wKF;AhzKpNn8~7H!jql*Qcojdy>-Z6V zjGyBdVrh{wi^oK}T;i8hTh5|<+aiN^V=;Q9PTTRnviSWv0pJ65o3=Hzw8r;7o>+Q- zcRaFXRNW3zK##y9z^05v!0Q5Gx;eT<+ghghPP+REG;>nI2oRb?;0ItJVgxe4BBm7LL{YjVsA+`ba0I^1F!e-n>n8mRjJFyE7VS-TOpq;>{ zu@{GN1kd1E!fl*zn?M^KEa5cH;4EQx0bj+}@O8Y1m+&%Pk@&qY&{_iSXc;vj@#=Em zrqTt+cAYWmEhDN0}#XH!o!1LPc;LCmCGFNy_VZU>hS-?vs#mUP$Vp^q>C`Ab-V%=YM$q2LRru F#os2_>W%;a literal 0 HcmV?d00001 diff --git a/tests/test_data/awg/TestAPS2/GST/listOfExperiments.npy b/tests/test_data/awg/TestAPS2/GST/GST/listOfExperiments.npy similarity index 100% rename from tests/test_data/awg/TestAPS2/GST/listOfExperiments.npy rename to tests/test_data/awg/TestAPS2/GST/GST/listOfExperiments.npy diff --git a/tests/test_data/awg/TestAPS2/GST2Q/listOfExperiments.npy b/tests/test_data/awg/TestAPS2/GST/GST2Q/listOfExperiments.npy similarity index 100% rename from tests/test_data/awg/TestAPS2/GST2Q/listOfExperiments.npy rename to tests/test_data/awg/TestAPS2/GST/GST2Q/listOfExperiments.npy From a9bf3ba30c472fdf286ca0c86e1f5133955eec12 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 27 Sep 2018 17:07:48 -0400 Subject: [PATCH 005/235] Fix python version error in travis --- .travis.yml | 3 ++- tests/test_data/awg/TestAPS1/.DS_Store | Bin 8196 -> 0 bytes tests/test_data/awg/TestAPS2/.DS_Store | Bin 8196 -> 0 bytes tests/test_data/awg/TestAPS2/GST/.DS_Store | Bin 8196 -> 0 bytes 4 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 tests/test_data/awg/TestAPS1/.DS_Store delete mode 100644 tests/test_data/awg/TestAPS2/.DS_Store delete mode 100644 tests/test_data/awg/TestAPS2/GST/.DS_Store diff --git a/.travis.yml b/.travis.yml index c8206609..a76dc173 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,7 @@ language: python python: - 3.5 + - 3.6 before_install: # install git lfs and fetch test data @@ -38,7 +39,7 @@ install: - source activate test-environment - conda install -c ecpy atom; - pip install watchdog coveralls - - pip install pygsti + - if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install pygsti; fi script: - coverage run -m unittest discover diff --git a/tests/test_data/awg/TestAPS1/.DS_Store b/tests/test_data/awg/TestAPS1/.DS_Store deleted file mode 100644 index d5cf7472eb5ae76255b7f9758306f85608b99e57..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeI0Piz!b7{I@8TVP%R`-&|c7SN5gw%`K&1Eqk)ZM(EZL6F_jQqXpGXJ{u*XV#tB z{WGPkq ziOO6QNX7!02;{7kq@je;m85jV;D~|Jo$6zOT_TXPQc8Eg;P8P#&lsFg(C?kvkNM_+ z2`R%oj6fKHr4bOHO$l^E3euVJh39w6biIa#I~XY`U9)zbqLh`DZ7h2(HR6n?Jk2Y( z9a{dZcsr?iBblI_b{uQ2tYuD{#`qww>~w5TGi@X9JDa*eCg=K0TX)7goPw>pemg0t zP!z`4Ag`XD-XD+cYuP&!k4-l=HMhiLt$UkhW|&f4+t_w=XmoOF`lXo*@5%{b@Kb>D zMH75)esGbTJy8gQMw*XbMf zo8&9%2Kgr8eY2g)#pXe$28YFkuz)>)y<&$4jR=N zp=;N5F(#!ZQ7@OfCea`ZTZzFARfXbK845dI6i}YIEWf% zFpCxzZ~`ZB3SY%H@hyBC-@y;@Q@ny#@iV-J*X76}MM96t_hOD;QE8sT;QlPqw;YGj zuFCGZ|AoW58zg)WR#rV!Qy*_^Ih2@NyhY1(4^ayt^2w2C_{bEI@L9c0h;E5)W!sAc zFDAQ}ATuTuK?+|dtz*$g6oP{*{wu4E)-fWOqtw<$A6FC}k-FW{21d~G5~*uu`xL^H zuaVkTc90Rkyi{u2*pmuz%GXM52TL;XDREc^xD|2x2(G}FgxW2*13waGzY$`85MFDM z`*=lhD{jZf2(cZw6XUoKTdb&yo8tWjR3t5Bvx~<^%OCa59z&YV;K}xsjEAp<}7M zZ8`QZwNeCKb{XcYI07YdY^pf_A6fbSe-+Ol99kHGFarNJ0$7?%B|E5D@jcyMxQ(@s-sC7s*k9drZ? z1PlZW1PlZW1PuHa7@&7HPtq3eePIpCfPsL4ZOH(CKEx<RcVxS18{+REV@Nr3?2nX~JALx&a{s{%W(dqgz&mAx! zFen2C0tPlSz;B-t7=$E@rRQ%xzng~RwzhtYp_0s2`pLA8X;Pk5b z8QvOI-MO@1AG2+9y{@Jw41GSus{3rqRSiqed%=dLlgsR+VQKb!uU)V-$Gc8a7$ixS z;zeH zbNgh*HVawHNwLa|YGw+i>gqkFnKUly&XlX4cX{o!W9QvI&oRYV!O>6YFc)ye3VW>{xW6Hk_F zJZ9*5xia**JZ7m`-HQrG!nG2sm!=A7<6OaTFRZdX73KTu!pf{hLq2S4JW699O0s8( z)rG?|+|rlVsnjG%4BilKl$k1xIW#-b)2zg0=E!4@)24}`?p4|(rpe=`n$}Ix)uD7s z>_K_5C|DDnN0sAJCC25cjGfKWyd7`k6ZeP1kMfZpQXO|Br&~0u6&jw;x0hx$&22rJ z#^)r}Cz`~r%Jqp>e}5&Yg?s3oGz7DtLk=#%Ww-)Y;dOWiuE7WJDSQDd@Fjc&U&HtC z1N;a-!7uO|`~iO=Afkd{j9?Akg$MC&Y`|tbj2+mCUD%BScoK(j7)Njlr*Q^nQNt`+ zcn=o;kHx$~YdReE=jj7}Miu`qBOUr*$wHI2(_wU= zS{eLzbokwFF22gDJF52{sBb*fdgRyx>-)B0yKYg`%_8ztBV^&pWlau3rr}6U?Y{j= zbex2wpWi6$HOaG`Tr$#XZKNg~jYSViBn~E;N<&nZNx{O_5N(zu7Fy+(jkiPqKB-&?SP0(#9``}9eHr)*z7V;` diff --git a/tests/test_data/awg/TestAPS2/GST/.DS_Store b/tests/test_data/awg/TestAPS2/GST/.DS_Store deleted file mode 100644 index 3369fa11e47e430935191879c3425e5589fb46f0..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 8196 zcmeI0O>h)N6oB8`{4g{8q=Uf>OMtD!6j1|Ylkg)F*lbpW5+t(u2MoJAV=`uTChp8; zLt?Dx0ZZiyC6;>dAZ2k9e@<%Yh2rEvQwF_Q9;~t~_29*;ucvz>A>^o1#dKBo>+bhn z|GfETdV2u?lSLyApaXzHnM<~oin}DP=Xphn$R4gl@`rdj=jJC@*o7To1i}b}5eOp? zMj(v9y%PaCvw2chIroJ&EW-$d5x6fSz`qYM%3LO-oEI|qbWjmo0Z3L-caW&gc!gw4 z%7m2jLXw6O3RjZC73CEJg*)}fq+LSFc_D>6puBvboEhZ}1%ugX{8(TPm=H27!w7^C zSQ!ES+El=kkcK{cY{mUuYVvL>Wfc7!z~VOx>Msrd_~cz<70u-MiqWdXJkSo$WJ}&; z)^@Wr(#`CoZFzoM+ag9PDyyp3D2k?O>$S7#akrHAGk(!aW(ucyt2g71XJx(5b?v2k zCOcr6rBPPb<2ruEa?C>DY#An*oEfwn!!0G@ZR& z@9|D{#_-40l zb7ODFH>Z7G8}ZzN-xC;)GFJ4=QzqG2Q)Y^+$#o+i>ExtuxsEqr7Ce$oi8irh_1d~k z(d|twJKOgi>|R*Ep@!9N)U*)_jpJL-S!SWXWEuYWaK_7-j$t`tgHx7gWo@(1$>h!2 z$PHBuydJFA^`T|3F;7RQrVmbut+A(M=l;hueTe>w z%F|Z7T5OfwU59l2Sw8dejOU-6G#y&iO*ws%RyE9Z-G;%}q%IrubizUC zg<&wD1Q+2FT!#1H8hi?$!43EtzJ>2#5pKax@GJZVzr!E!7gl05u0aKBaVu`a?bw1l zaW}SM91q}O?8YSa;Bh>Keb|o!IErIv;qy3wKF;AhzKpNn8~7H!jql*Qcojdy>-Z6V zjGyBdVrh{wi^oK}T;i8hTh5|<+aiN^V=;Q9PTTRnviSWv0pJ65o3=Hzw8r;7o>+Q- zcRaFXRNW3zK##y9z^05v!0Q5Gx;eT<+ghghPP+REG;>nI2oRb?;0ItJVgxe4BBm7LL{YjVsA+`ba0I^1F!e-n>n8mRjJFyE7VS-TOpq;>{ zu@{GN1kd1E!fl*zn?M^KEa5cH;4EQx0bj+}@O8Y1m+&%Pk@&qY&{_iSXc;vj@#=Em zrqTt+cAYWmEhDN0}#XH!o!1LPc;LCmCGFNy_VZU>hS-?vs#mUP$Vp^q>C`Ab-V%=YM$q2LRru F#os2_>W%;a From a5ec75d2aeae2b531eef2dd5d70243eaa1623225 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 27 Sep 2018 20:04:54 -0400 Subject: [PATCH 006/235] added some missing files --- tests/test_data/awg/TestAPS1/GST/.DS_Store | Bin 10244 -> 0 bytes .../awg/TestAPS1/GST/GST/listOfExperiments.npy | Bin 0 -> 33634 bytes .../TestAPS1/GST/GST2Q/listOfExperiments.npy | Bin 0 -> 138231 bytes 3 files changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/test_data/awg/TestAPS1/GST/.DS_Store create mode 100644 tests/test_data/awg/TestAPS1/GST/GST/listOfExperiments.npy create mode 100644 tests/test_data/awg/TestAPS1/GST/GST2Q/listOfExperiments.npy diff --git a/tests/test_data/awg/TestAPS1/GST/.DS_Store b/tests/test_data/awg/TestAPS1/GST/.DS_Store deleted file mode 100644 index 15ab269470ef39663906285b66580d61030baf54..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 10244 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

HrFS)ejNH3i%p>Q&GLPJQQvW{c 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 Date: Sat, 5 Jan 2019 19:37:44 -0500 Subject: [PATCH 007/235] Remove unncessary .DS_Store file --- tests/test_data/awg/TestAPS1/GST/GST/.DS_Store | Bin 6148 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 tests/test_data/awg/TestAPS1/GST/GST/.DS_Store diff --git a/tests/test_data/awg/TestAPS1/GST/GST/.DS_Store b/tests/test_data/awg/TestAPS1/GST/GST/.DS_Store deleted file mode 100644 index 5008ddfcf53c02e82d7eee2e57c38e5672ef89f6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 6148 zcmeH~Jr2S!425mzP>H1@V-^m;4Wg<&0T*E43hX&L&p$$qDprKhvt+--jT7}7np#A3 zem<@ulZcFPQ@L2!n>{z**++&mCkOWA81W14cNZlEfg7;MkzE(HCqgga^y>{tEnwC%0;vJ&^%eQ zLs35+`xjp>T0 Date: Thu, 10 Jan 2019 10:18:18 -0500 Subject: [PATCH 008/235] Change np.isclose -> np.all(np.less()) in APS2 pattern generator --- QGL/drivers/APS2Pattern.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 64dd96c1..1f174564 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -619,11 +619,11 @@ def inject_modulation_cmds(seqs): freqs = np.unique([entry.frequency for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)]) if len(freqs) > 2: raise Exception("Max 2 frequencies on the same channel allowed.") - no_freq_cmds = np.allclose(freqs, 0) + no_freq_cmds = np.all(np.less(np.abs(freqs), 1e-8)) phases = [entry.phase for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] - no_phase_cmds = np.allclose(phases, 0) + no_phase_cmds = np.all(np.less(np.abs(phases), 1e-8)) frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] - no_frame_cmds = np.allclose(frame_changes, 0) + no_frame_cmds = np.all(np.less(np.abs(frame_changes), 1e-8)) no_modulation_cmds = no_freq_cmds and no_phase_cmds and no_frame_cmds if no_modulation_cmds: From 476520134cef38cbf3e6a089c59f59dbd8ed6509 Mon Sep 17 00:00:00 2001 From: gribeill Date: Thu, 10 Jan 2019 10:41:19 -0500 Subject: [PATCH 009/235] Simple script to look at cProfile info. --- tests/test_perf.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_perf.py diff --git a/tests/test_perf.py b/tests/test_perf.py new file mode 100644 index 00000000..aea99755 --- /dev/null +++ b/tests/test_perf.py @@ -0,0 +1,18 @@ +#simple file added for performace testing... + +from QGL import * +import random + +N = 1000 + +cl = ChannelLibrary() +q1 = QubitFactory("q1") + +gates = (X90(q1), Y90(q1), X(q1), Y(q1), Id(q1, length=1e-7)) + +seqs = [] +ks = random.choices(range(1,100), k = N) +for k in ks: + seqs.append(random.choices(gates, k=k) + [MEAS(q1)]) + +compile_to_hardware(seqs, 'test/test') From fd7b5f8cd048ca9d93fe5422b0d1c7c4153da528 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Fri, 4 Jan 2019 17:02:57 -0500 Subject: [PATCH 010/235] Update README.md QGL compiler needs networkx 2.0 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 77a91176..34c0da2a 100644 --- a/README.md +++ b/README.md @@ -60,6 +60,6 @@ The QGL config file will be created the first time you run `import QGL` or `from * h5py * watchdog * Bokeh 0.11 -* networkx +* networkx 2.0 * iPython/Jupyter 4.0 (only for Jupyter notebooks) * ruamel_yaml From dfc91e608eb8fd9b7d6f2f8bdd3eec5b895f6ece Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Fri, 4 Jan 2019 17:03:58 -0500 Subject: [PATCH 011/235] Update setup.py The compiler needs networkx 2.0 or higher now for edge identification. --- setup.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 3a7ee80b..dee9e96b 100644 --- a/setup.py +++ b/setup.py @@ -10,7 +10,7 @@ "jupyter >= 1.0.0", "atom >= 0.4.1", "h5py >= 2.6.0", - "networkx >= 1.11", + "networkx >= 2.0", "future >= 0.16", "watchdog >= 0.8.3", "bokeh >= 0.11", From 52251f637bb0162504f134f7a823460b105e97b5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Mon, 30 Jul 2018 10:47:51 -0400 Subject: [PATCH 012/235] Consistent order (qc,qt) for CR Consistent order for all standard sequences, including two-qubit RB and cal_seqs --- QGL/BasicSequences/CR.py | 8 ++++++-- QGL/BasicSequences/RB.py | 2 +- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/QGL/BasicSequences/CR.py b/QGL/BasicSequences/CR.py index e94cdda5..f5185b20 100644 --- a/QGL/BasicSequences/CR.py +++ b/QGL/BasicSequences/CR.py @@ -80,7 +80,11 @@ def EchoCRLen(controlQ, echoCR(controlQ, targetQ, length=l, phase= phase, amp=amp, riseFall=riseFall, canc_amp=canc_amp, canc_phase=canc_phase), X(controlQ), MEAS(targetQ)*MEAS(controlQ)] for l in lengths] + \ +<<<<<<< HEAD create_cal_seqs((targetQ,controlQ), calRepeats, measChans=(targetQ, controlQ)) +======= + create_cal_seqs((controlQ,targetQ), calRepeats, measChans=(targetQ,controlQ)) +>>>>>>> 3e7c872... Consistent order (qc,qt) for CR metafile = compile_to_hardware(seqs, 'EchoCR/EchoCR', axis_descriptor=[ @@ -126,7 +130,7 @@ def EchoCRPhase(controlQ, echoCR(controlQ, targetQ, length=length, phase= ph, amp=amp, riseFall = riseFall, canc_amp=canc_amp, canc_phase=canc_phase), X90(targetQ)*X(controlQ), MEAS(targetQ)*MEAS(controlQ)] for ph in phases] + \ - create_cal_seqs((targetQ,controlQ), calRepeats, measChans=(targetQ,controlQ)) + create_cal_seqs((controlQ, targetQ), calRepeats, measChans=(targetQ,controlQ)) axis_descriptor = [ { @@ -177,7 +181,7 @@ def EchoCRAmp(controlQ, echoCR(controlQ, targetQ, length=length, phase= phase, riseFall=riseFall,amp=a), X(controlQ), MEAS(targetQ)*MEAS(controlQ)] for a in amps] + \ - create_cal_seqs((targetQ,controlQ), calRepeats, measChans=(targetQ,controlQ)) + create_cal_seqs((controlQ ,targetQ), calRepeats, measChans=(targetQ,controlQ)) axis_descriptor = [ { diff --git a/QGL/BasicSequences/RB.py b/QGL/BasicSequences/RB.py index 6075445d..11285889 100644 --- a/QGL/BasicSequences/RB.py +++ b/QGL/BasicSequences/RB.py @@ -99,7 +99,7 @@ def TwoQubitRB(q1, q2, seqs, showPlot=False, suffix="", add_cals=True): """ seqsBis = [] for seq in seqs: - seqsBis.append(reduce(operator.add, [clifford_seq(c, q1, q2) + seqsBis.append(reduce(operator.add, [clifford_seq(c, q2, q1) for c in seq])) #Add the measurement to all sequences From 20391fbb8fd9eb62976144a4e2e4f135c98c2ff8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Fri, 4 Jan 2019 16:36:39 -0500 Subject: [PATCH 013/235] Fix test sequences --- tests/test_Sequences.py | 4 ++-- tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 | 4 ++-- tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 | 4 ++-- tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 | 4 ++-- .../test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 | 4 ++-- .../test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 | 4 ++-- .../test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 | 4 ++-- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 | 4 ++-- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 | 4 ++-- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 | 4 ++-- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 | 4 ++-- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 | 4 ++-- .../test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 | 4 ++-- .../test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 | 4 ++-- .../test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 | 4 ++-- .../test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 | 4 ++-- .../test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 | 4 ++-- 17 files changed, 34 insertions(+), 34 deletions(-) diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index 59c4fff8..1531ed85 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -343,8 +343,8 @@ def test_RB_TwoQubitRB(self): """ self.set_awg_dir('TwoQubitRB') np.random.seed(20152606) # set seed for create_RB_seqs() - TwoQubitRB(self.q1, - self.q2, + TwoQubitRB(self.q2, + self.q1, create_RB_seqs(2, [2, 4, 8, 16, 32], repeats=16)) self.compare_sequences('RB') diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 index 27ff6fb2..0f589d2c 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f850c54af8a59fcf856f7618fae36f5ae3821821ed6ceef6f9aced501c27e764 -size 20568 +oid sha256:f1e0f1039e90c8361735fe98ddbd24e434fb9b760169a62ec946ae40274d1a60 +size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 index 17d18bd4..b5872b5f 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:20b8f27f2d043c25e5de95a4dcd84777de6afc51050ffaff19db10fd06cf8090 -size 20568 +oid sha256:92b69fd2c1d4208e22cb652dfac0e78de7d7502fc1a0028e4df32c7319cd9d02 +size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 index 94abd59e..953bfa04 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:59d42d07df162b866a418c6cab57d312fc90e3db9363d6d1171b4d1de0d7627c -size 18152 +oid sha256:aa055382df427f2625eb61ec6c112603c36891bf69e2e2d5037e2ef0af8d725b +size 18136 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 index 8c321d08..9c6f0102 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:a2a647c54663b03e82d4f2786af4383a0a16b24150b42e8aaf90dd8062febc7c -size 20568 +oid sha256:e1bc671bcfbba1005d9e593edf45288d0b6d830ff0445cf6c9360c31daa0753b +size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 index 6e676ef3..041cb89c 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:683250087fef581c4581f78c707450224ff79dcea113e667a3cd895835fd6851 -size 20568 +oid sha256:8e1b9e57467e487b23c15743b839e640c168bb5b0fe48fc55df83ade33e69558 +size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 index de0e0e3b..9898ef3b 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:fc79b65361c3b9d752eb5bb80bb2be2cebc44c135289fe41ae351ed9b3573e11 -size 26968 +oid sha256:c5e77f05e76a9b655cccc9c76aae4f3e412c88f4e7259b18db9a2a1b21c0fe4e +size 26992 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 index 9f64875d..ab15aa85 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:94db39782a1472bd2b30e94e24e494ed4deb21a9fa333a90023126cb94d726f0 -size 12888 +oid sha256:074d02801fc6b663db6f9250ceb639ca7b2e755ddb04ae70f3b9dd87e50c586c +size 12792 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 index 7b3fb1f2..9d8b12af 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dce79ac838eb4988853cf333af4d42d084f7a07ced8d3f7cb1098e451c94282 -size 11440 +oid sha256:61084d6ac1164b7568c446759cb0baca6d850926162b7adc3073d4caa2aed0c7 +size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 index 392ee3e1..0ab91628 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0d8988518d9840a29dc25aa0ce90082f5d7a5ba572d70a269b0f0cdb5d20e94c -size 10896 +oid sha256:6749480ab0b75672d39dd6005238719b1e76e3b3f9e6814894220076158ee1aa +size 10800 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 index 7b3fb1f2..9d8b12af 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1dce79ac838eb4988853cf333af4d42d084f7a07ced8d3f7cb1098e451c94282 -size 11440 +oid sha256:61084d6ac1164b7568c446759cb0baca6d850926162b7adc3073d4caa2aed0c7 +size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 index e4233b17..fae1d541 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:721c25cf12aac47cebabee78faca114685cacbfe456a0b0ce3ab1fbf81d6d4d0 -size 14288 +oid sha256:e7da7ea9ae19ea57b6e1cc5783c4ebb53ad2522ef51979d1fb656738ecc0aaf6 +size 14192 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 index c919b2a9..8696d76b 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7547dc35bdf655324e21fe90f4d25fbcd463b9021e5cfa41dcfbc86e0afcdf38 -size 12888 +oid sha256:aace28a1695a9111804ab0bec5d783d482b835646ef86ca46e8e72b95092c8eb +size 12792 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 index 39f1f02d..0779fc89 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b916c1828d45b3e9d2cd7f519e8f8c97c0b5ced8fd5c65dcb672b16865dcc5b6 -size 11440 +oid sha256:31849839f1cef008563c60475daae27687e16d87fd14f793eabdf80b4140cdc6 +size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 index 67fc1946..3944eb32 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e0377243cba052e4b941c8408ecf3c56d7f0ea73318d7291c89e53bdaadb2f62 -size 11392 +oid sha256:f4e95177c552f582aa32d9fac036db4cc9acd19214eabb4d74bec2a31fbb885f +size 11296 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 index 39f1f02d..9220854d 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b916c1828d45b3e9d2cd7f519e8f8c97c0b5ced8fd5c65dcb672b16865dcc5b6 -size 11440 +oid sha256:f2000631a2233d3c5b19d2fa7e53d5c6b65f3a09433bd0b8aedc85906d9bccc3 +size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 index 23d10721..043f2ada 100644 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:65700d329e19e140b318a67fc46e3f3b72039f4536b0fdec05e28e53fde17f25 -size 22336 +oid sha256:4f8df32e2cf8c12ad3b48430f82500f8aa5fcdbf20d321825ae1f487d6f091db +size 22240 From 8fc971e6595efd39972f80641cbf1367e096b958 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Wed, 7 Nov 2018 10:19:53 -0500 Subject: [PATCH 014/235] Allow for slave trigger for APS1 Even with the TDM triggering, we need a slave trigger for the APS1s --- QGL/ChannelLibraries.py | 45 ++++++++++++++++++++++------------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index c84de4e7..6afb03b2 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -265,32 +265,35 @@ def load_from_library(self, return_only=False): else: master_awgs.append(name) # Eventually we should support multiple masters... - # if "slave_trig" in instr.keys(): - # params = {} - # params["label"] = name + "_slave_trig" - # params["phys_chan"] = name + "-" + instr["slave_trig"] - # params["pulse_params"] = {"length": 1e-7, "shape_fun": "constant"} - # params["__module__"] = "QGL.Channels" - # params["__class__"] = "LogicalMarkerChannel" - # print(params["label"], "***") - # channel_dict[params["label"]] = params + if "slave_trig" in instr.keys(): + params = {} + params["label"] = "slave_trig" + params["phys_chan"] = name + "-" + instr["slave_trig"] + if params["phys_chan"] in marker_lens.keys(): + length = marker_lens[params["phys_chan"]] + else: + length = 1e-7 + params["pulse_params"] = {"length": length, "shape_fun": "constant"} + params["__module__"] = "QGL.Channels" + params["__class__"] = "LogicalMarkerChannel" + channel_dict[params["label"]] = params # Establish the slave trigger, assuming for now that we have a single # APS master. This might change later. if len(master_awgs) > 1: raise ValueError("More than one AWG is marked as master.") - elif len(master_awgs) == 1 and instr_dict[master_awgs[0].split('-')[0]]['type'] != 'TDM': - params = {} - params["label"] = "slave_trig" - params["phys_chan"] = master_awgs[0] - if params["phys_chan"] in marker_lens.keys(): - length = marker_lens[params["phys_chan"]] - else: - length = 1e-7 - params["pulse_params"] = {"length": length, "shape_fun": "constant"} - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - channel_dict[params["label"]] = params + # elif len(master_awgs) == 1 and instr_dict[master_awgs[0].split('-')[0]]['type'] != 'TDM': + # params = {} + # params["label"] = "slave_trig" + # params["phys_chan"] = master_awgs[0] + # if params["phys_chan"] in marker_lens.keys(): + # length = marker_lens[params["phys_chan"]] + # else: + # length = 1e-7 + # params["pulse_params"] = {"length": length, "shape_fun": "constant"} + # params["__module__"] = "QGL.Channels" + # params["__class__"] = "LogicalMarkerChannel" + # channel_dict[params["label"]] = params for name, filt in filter_dict.items(): if "StreamSelector" in filt["type"]: From 09ff7eefe3c30680da3ed8c9e941b8f61a8abc5d Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Wed, 7 Nov 2018 18:03:19 -0500 Subject: [PATCH 015/235] Added a __main__ reference to get simple python invocation unittest working Re: python test_Scheduler.py -v --- tests/test_Scheduler.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/test_Scheduler.py b/tests/test_Scheduler.py index e29a1e25..003b5f4e 100644 --- a/tests/test_Scheduler.py +++ b/tests/test_Scheduler.py @@ -121,3 +121,6 @@ def test_measurements(self): # should be unchanged assert seq == result + +if __name__ == "__main__": + unittest.main() From 8505202ae233b69fb11ab92a75dc452b4113f9ac Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Fri, 9 Nov 2018 12:07:34 -0500 Subject: [PATCH 016/235] Added test_QGL comments regarding Git LFS data file usage (and error encountered when git-lfs library is NOT installed). --- tests/test_QGL.py | 14 ++++++++++++++ tests/test_pulse_types.py | 9 ++++++++- 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/tests/test_QGL.py b/tests/test_QGL.py index 89615e54..b91156b0 100644 --- a/tests/test_QGL.py +++ b/tests/test_QGL.py @@ -88,7 +88,21 @@ def load(self): 'Warning: valid waveform file for {0} not found at: {1}'.format( caseName, fileName)) continue + # ----- + # print( "\n\rDBG::Calling \"with h5py.File( {0}, 'r')\"...".format( fileName) ) + # ----- + # The following pulls in Git Large File Storage (LFS) data files + # from cached signature references; if the h5py.File call fails + # with an OSError, double-check git-lfs library installation (in + # addition to git) -- + # + # Where git-lfs was NOT installed the h5py.File() call was observed + # returning: + # + # OSError: Unable to open file (file signature not found)) + # with h5py.File(fileName, 'r') as FID: + # print( "DBG::FID: {0}".format( FID)) for name, waveform in FID['/channels'].items(): validWaveform[name] = waveform[:] self.validWaveforms[caseName] = validWaveform diff --git a/tests/test_pulse_types.py b/tests/test_pulse_types.py index 69d8221f..7f574b4a 100644 --- a/tests/test_pulse_types.py +++ b/tests/test_pulse_types.py @@ -16,7 +16,9 @@ def setUp(self): self.q3 = QubitFactory('q3') self.q4 = QubitFactory('q4') - @unittest.skip("Type promition for CNOT(q1, q2) * X(q3) gives PulseBlock not CompoundGate. Looking into this issue.") + # This appears to run successfully with the skip commented out; + # TJR, 07 Nov 2018 + #@unittest.skip("Type promition for CNOT(q1, q2) * X(q3) gives PulseBlock not CompoundGate. Looking into this issue.") def test_promotion_rules(self): q1, q2, q3, q4 = self.q1, self.q2, self.q3, self.q4 @@ -28,3 +30,8 @@ def test_promotion_rules(self): assert( type(CNOT_CR(q1, q2) * X(q3)) == CompoundGate ) assert( type(X(q3) * CNOT_CR(q1, q2)) == CompoundGate ) assert( type(CNOT_CR(q1, q2) * CNOT_CR(q3, q4)) == CompoundGate ) + +# Added to support simple python invocation +# +if __name__ == "__main__": + unittest.main() From a3654d2333bfe6bdc23bbc31d697e6f79cab91e6 Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Fri, 9 Nov 2018 12:27:18 -0500 Subject: [PATCH 017/235] Added README.md tail comment regarding Git LFS usage for some unittest data files. --- README.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/README.md b/README.md index 34c0da2a..57b51930 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,7 @@ The QGL config file will be created the first time you run `import QGL` or `from * networkx 2.0 * iPython/Jupyter 4.0 (only for Jupyter notebooks) * ruamel_yaml + +## UnitTest data support +This repository uses the Git Large File Storage (LFS) extension to manage a few +UnitTest data files (see https://git-lfs.github.com/). From 86f36b9993b0af515a322004af8bb13ff2b567fd Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Wed, 14 Nov 2018 17:25:53 -0500 Subject: [PATCH 018/235] Defined ReadMeDocs.txt to briefly describe the MkDocs document library use case. --- ReadMe_Docs.txt | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 ReadMe_Docs.txt diff --git a/ReadMe_Docs.txt b/ReadMe_Docs.txt new file mode 100644 index 00000000..0e42164e --- /dev/null +++ b/ReadMe_Docs.txt @@ -0,0 +1,6 @@ +#---------- 14 Nov 2018 +This repository uses the MkDocs (v0.17.2) utilities for documentation rendering. +From the repo root directory, "mkdocs --help" shows the options. +"mkdocs build" for example, writes a set of html documents under the ./site subdirectory + +(Aside: ./site is NOT currently in the .gitignore file; insertion pending for consistency). From 75b4ef9e4eef6d06d7b14da11d004c54720d4934 Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Fri, 30 Nov 2018 13:12:02 -0500 Subject: [PATCH 019/235] Defined renderDocs.sh and runUnitTests.sh shell scripts to automate documentation rendering and unit test invocation logic. --- renderDocs.sh | 26 ++++++++++++++++++ runUnitTests.sh | 72 +++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 98 insertions(+) create mode 100755 renderDocs.sh create mode 100755 runUnitTests.sh diff --git a/renderDocs.sh b/renderDocs.sh new file mode 100755 index 00000000..04b03d12 --- /dev/null +++ b/renderDocs.sh @@ -0,0 +1,26 @@ +#!/bin/bash +echo "" +echo "#---------- $0 (QGL) start..." +echo "" +#----- Simple script to capture basic documentation rendering logic. + +echo "#-----Building QGL docs via ~:" + +export CMD="mkdocs build" +echo $CMD +echo "" +$CMD + +pushd . + +cd site +echo "" +echo "Target QGL documents list as follows:" +pwd +ls -l + + +echo "" +echo "#---------- $0 (QGL) stop." +echo "" + diff --git a/runUnitTests.sh b/runUnitTests.sh new file mode 100755 index 00000000..d063b6bf --- /dev/null +++ b/runUnitTests.sh @@ -0,0 +1,72 @@ +#!/bin/bash +echo "" +echo "#---------- $0 (QGL) start..." +echo "" +#----- Simple script to capture basic documentation rendering logic. + +pwd + +export szGit_LFS_Path=`which git-lfs` +echo "szGit_LFS_Path: [${szGit_LFS_Path}]" + + +# Instantiate the git lfs cached data files... +export CMD="git lfs pull" +echo "" +if [ -z ${szGit_LFS_Path} ]; then + echo "" + echo "#--------------------" + echo "# W A R N I N G -- git-lfs extension library unavailable;" + echo "# -- QGL h5 data file processing may fail." + echo "# << [${szGit_LFS_Path}] << 'which git-lfs'" + echo "#--------------------" +else + echo "git-lfs available; retrieving/instantiating cached QGL h5 data files via ~:" + echo $CMD + echo "" + $CMD +fi +echo "" + + +# Don't forget to set the BBN_MEAS_FILE reference. +export BBN_MEAS_FILE=tests/test_measure.yml + +echo "" +echo "#-----Unit-Testing general QGL (BBN_MEAS_FILE=${BBN_MEAS_FILE}) via ~:" +# -f option fails fast +#export CMD="python -m unittest discover . -v -f" +#export CMD="python -m unittest discover . -v" +# Trimm'ed down (non LFS unitest calls) syntax as follows: +export CMD="python -m unittest tests/test_A*.py tests/test_C*.py tests/test_Scheduler.py tests/test_config.py tests/test_pulse_types.py -v" +echo $CMD +echo "" +$CMD + +if [ -z ${szGit_LFS_Path} ]; then + echo "" + echo "#----- W A R N I N G:" + echo " Fast-failing unittest modules test_QGL.py, and test_Sequences.py," + echo " currently, due to git lfs data file dependencies; in-short, the" + echo " docker continuumio/miniconda load appears to omit the necessary" + echo " git-lfs library installation." + echo "" + echo " Without the git-lfs library extension, h5 data calls from these" + echo " modules typically error out with ~:" + echo " OSError: Unable to open file (file signature not found)" + echo "" + echo " If/when the git-lfs library becomes available in the docker load, " + echo " remove the \"-f\"from the invocation, below:" +fi + +echo "" +echo "#----- Testing LFS data file dependent QGL modules via ~:" +export CMD="python -m unittest tests.test_QGL tests.test_Sequences.py -v -f" +echo $CMD +echo "" +$CMD + +echo "" +echo "#---------- $0 (QGL) stop." +echo "" + From 00ecab37dc20252b034f58fa6f0ec28ee7eb4796 Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Fri, 30 Nov 2018 13:22:22 -0500 Subject: [PATCH 020/235] Corrected runUnitTests.sh description header. --- runUnitTests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/runUnitTests.sh b/runUnitTests.sh index d063b6bf..5ff573ab 100755 --- a/runUnitTests.sh +++ b/runUnitTests.sh @@ -2,7 +2,7 @@ echo "" echo "#---------- $0 (QGL) start..." echo "" -#----- Simple script to capture basic documentation rendering logic. +#----- Simple script to capture unittest preparation and invocation logic. pwd From a2153f640480d7d282b865d3c5819bba3d4d4ec5 Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Tue, 4 Dec 2018 14:28:54 -0500 Subject: [PATCH 021/235] Fixed a typo in runUnitTests.sh (removed the .py reference in second test set). Added test_Sequences.py logic (set_awg_dir) to invoke config.load_conifg() when config.AWGDir is undefined. (when running as a stand-alone test, this case was causing failures). --- runUnitTests.sh | 4 +++- tests/test_Sequences.py | 12 ++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/runUnitTests.sh b/runUnitTests.sh index 5ff573ab..ffab4f0f 100755 --- a/runUnitTests.sh +++ b/runUnitTests.sh @@ -61,7 +61,9 @@ fi echo "" echo "#----- Testing LFS data file dependent QGL modules via ~:" -export CMD="python -m unittest tests.test_QGL tests.test_Sequences.py -v -f" +# Careful -- in this format (tests.moduleName) DON'T cite the .py suffix +# (it will render an odd error regarding missing 'py' attribute) +export CMD="python -m unittest tests.test_QGL tests.test_Sequences -v -f" echo $CMD echo "" $CMD diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index 1531ed85..ada1e278 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -9,6 +9,11 @@ from QGL.Channels import Edge, Measurement, LogicalChannel, LogicalMarkerChannel, PhysicalMarkerChannel, PhysicalQuadratureChannel from QGL.drivers import APSPattern, APS2Pattern, TekPattern +# Pulled in logger to help debug stand-alone run issue with the config.AWGDir +# (the configuration was NOT gettng loaded when run independently) +# +import logging +logger = logging.getLogger( 'sequences') class AWGTestHelper(object): testFileDirectory = './tests/test_data/awg/' @@ -94,6 +99,13 @@ def get_qubits(self): def set_awg_dir(self, footer=""): cn = self.__class__.__name__ + if None == QGL.config.AWGDir: + logger.warning( "\n\r#----- EEE NULL QGL.config.AWGDir {%s} cited; calling load_config()...", QGL.config.AWGDir) + QGL.config.load_config() + logger.warning( "#----- Post load-config QGL.config.AWGDir: {%s}.\n\r", QGL.config.AWGDir) + #else: + # logger.warning( "\n\r#----- Using QGL.config.AWGDir {%s} 8-p", QGL.config.AWGDir) + self.awg_dir = os.path.abspath(QGL.config.AWGDir + os.path.sep + cn) self.truth_dir = os.path.abspath(self.testFileDirectory + os.path.sep + cn) From c9da2dc2e377bbc310c078e934514bca39a43b66 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Sat, 5 Jan 2019 19:14:29 -0500 Subject: [PATCH 022/235] fixed a mkdocs dep warning --- mkdocs.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkdocs.yml b/mkdocs.yml index 5d43ff40..6ff817c8 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,5 +1,5 @@ site_name: QGL -pages: +nav: - Intro: index.md - Configuration: config.md - Examples: examples.md From 8d9853ae45bd2e96bf88de49536afad1b01b8e26 Mon Sep 17 00:00:00 2001 From: gribeill Date: Thu, 10 Jan 2019 10:18:18 -0500 Subject: [PATCH 023/235] Change np.isclose -> np.all(np.less()) in APS2 pattern generator --- QGL/drivers/APS2Pattern.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 64dd96c1..1f174564 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -619,11 +619,11 @@ def inject_modulation_cmds(seqs): freqs = np.unique([entry.frequency for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)]) if len(freqs) > 2: raise Exception("Max 2 frequencies on the same channel allowed.") - no_freq_cmds = np.allclose(freqs, 0) + no_freq_cmds = np.all(np.less(np.abs(freqs), 1e-8)) phases = [entry.phase for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] - no_phase_cmds = np.allclose(phases, 0) + no_phase_cmds = np.all(np.less(np.abs(phases), 1e-8)) frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] - no_frame_cmds = np.allclose(frame_changes, 0) + no_frame_cmds = np.all(np.less(np.abs(frame_changes), 1e-8)) no_modulation_cmds = no_freq_cmds and no_phase_cmds and no_frame_cmds if no_modulation_cmds: From 5821b5e2b75ba04897fa96608fd17014faa4c443 Mon Sep 17 00:00:00 2001 From: gribeill Date: Thu, 10 Jan 2019 10:41:19 -0500 Subject: [PATCH 024/235] Simple script to look at cProfile info. --- tests/test_perf.py | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 tests/test_perf.py diff --git a/tests/test_perf.py b/tests/test_perf.py new file mode 100644 index 00000000..aea99755 --- /dev/null +++ b/tests/test_perf.py @@ -0,0 +1,18 @@ +#simple file added for performace testing... + +from QGL import * +import random + +N = 1000 + +cl = ChannelLibrary() +q1 = QubitFactory("q1") + +gates = (X90(q1), Y90(q1), X(q1), Y(q1), Id(q1, length=1e-7)) + +seqs = [] +ks = random.choices(range(1,100), k = N) +for k in ks: + seqs.append(random.choices(gates, k=k) + [MEAS(q1)]) + +compile_to_hardware(seqs, 'test/test') From 3c76064e70d1ed11fc6f54a7645e8bab73fe77de Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 10 Jan 2019 14:33:17 -0500 Subject: [PATCH 025/235] Add IR compile function --- QGL/Compiler.py | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 599e8d18..4c53d381 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -299,6 +299,124 @@ def collect_specializations(seqs): done.append(target) return funcs +def compile_to_qgl_IR(seqs, + suffix='', + axis_descriptor=None, + add_slave_trigger=True, + extra_meta=None, + tdm_seq = False): + ''' + Compiles 'seqs' to a hardware description and saves it to 'fileName'. + Other inputs: + suffix (optional): string to append to end of fileName, e.g. with + fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 + axis_descriptor (optional): a list of dictionaries describing the effective + axes of the measurements that the sequence will yield. For instance, + if `seqs` generates a Ramsey experiment, axis_descriptor would describe + the time delays between pulses. + add_slave_trigger (optional): add the slave trigger(s) + tdm_seq (optional): compile for TDM + ''' + logger.debug("Compiling %d sequence(s)", len(seqs)) + + # save input code to file + save_code(seqs, fileName + suffix) + + # all sequences should start with a WAIT for synchronization + for seq in seqs: + if not isinstance(seq[0], ControlFlow.Wait): + logger.debug("Adding a WAIT - first sequence element was %s", seq[0]) + seq.insert(0, ControlFlow.Wait()) + + # Add the digitizer trigger to measurements + logger.debug("Adding digitizer trigger") + PatternUtils.add_digitizer_trigger(seqs) + + # Add gating/blanking pulses + logger.debug("Adding blanking pulses") + for seq in seqs: + PatternUtils.add_gate_pulses(seq) + + if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: + # Add the slave trigger + logger.debug("Adding slave trigger") + PatternUtils.add_slave_trigger(seqs, + ChannelLibraries.channelLib['slave_trig']) + else: + logger.debug("Not adding slave trigger") + + # find channel set at top level to account for individual sequence channel variability + channels = set() + for seq in seqs: + channels |= find_unique_channels(seq) + + # Compile all the pulses/pulseblocks to sequences of pulses and control flow + wireSeqs = compile_sequences(seqs, channels) + + if not validate_linklist_channels(wireSeqs.keys()): + print("Compile to hardware failed") + return + + logger.debug('') + logger.debug("Now after gating constraints:") + # apply gating constraints + for chan, seq in wireSeqs.items(): + if isinstance(chan, Channels.LogicalMarkerChannel): + wireSeqs[chan] = PatternUtils.apply_gating_constraints( + chan.phys_chan, seq) + debug_print(wireSeqs, 'Gated sequence') + + # save number of measurements for meta info + num_measurements = count_measurements(wireSeqs) + wire_measurements = count_measurements_per_wire(wireSeqs) + + # map logical to physical channels, physWires is a list of + # PhysicalQuadratureChannels and PhysicalMarkerChannels + # for the APS, the naming convention is: + # ASPName-12, or APSName-12m1 + physWires = map_logical_to_physical(wireSeqs) + + # Pave the way for composite instruments, not useful yet... + files = {} + label_to_inst = {} + label_to_chan = {} + old_wire_names = {} + old_wire_instrs = {} + for wire, pulses in physWires.items(): + pattern_module = import_module('QGL.drivers.' + wire.translator) + if pattern_module.SEQFILE_PER_CHANNEL: + inst_name = pattern_module.get_true_inst_name(wire.label) + chan_name = pattern_module.get_true_chan_name(wire.label) + has_non_id_pulses = any([len([p for p in ps if isinstance(p,Pulse) and p.label!="Id"]) > 0 for ps in pulses]) + label_to_inst[wire.label] = inst_name + if has_non_id_pulses: + label_to_chan[wire.label] = chan_name + # Change the name/inst for uniqueness, but we must restore this later! + old_wire_names[wire] = wire.label + old_wire_instrs[wire] = wire.instrument + wire.instrument = wire.label + wire.label = chan_name + files[inst_name] = {} + + # construct channel delay map + delays = channel_delay_map(physWires) + + # apply delays + for chan, wire in physWires.items(): + PatternUtils.delay(wire, delays[chan]) + debug_print(physWires, 'Delayed wire') + + # generate wf library (base shapes) + wfs = generate_waveforms(physWires) + + # replace Pulse objects with Waveforms + physWires = pulses_to_waveforms(physWires) + + # bundle wires on instruments, or channels depending + # on whether we have one sequence per channel + awgData = bundle_wires(physWires, wfs) + + return awgData def compile_to_hardware(seqs, fileName, From 7f7e434905b1836e337160213ae5cac77317afd0 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 10 Jan 2019 16:56:09 -0500 Subject: [PATCH 026/235] Add memory info --- QGL/BasicSequences/CR.py | 4 ---- QGL/Compiler.py | 39 ++++++++++++++++++++++++++++++--------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/QGL/BasicSequences/CR.py b/QGL/BasicSequences/CR.py index f5185b20..39f26e45 100644 --- a/QGL/BasicSequences/CR.py +++ b/QGL/BasicSequences/CR.py @@ -80,11 +80,7 @@ def EchoCRLen(controlQ, echoCR(controlQ, targetQ, length=l, phase= phase, amp=amp, riseFall=riseFall, canc_amp=canc_amp, canc_phase=canc_phase), X(controlQ), MEAS(targetQ)*MEAS(controlQ)] for l in lengths] + \ -<<<<<<< HEAD - create_cal_seqs((targetQ,controlQ), calRepeats, measChans=(targetQ, controlQ)) -======= create_cal_seqs((controlQ,targetQ), calRepeats, measChans=(targetQ,controlQ)) ->>>>>>> 3e7c872... Consistent order (qc,qt) for CR metafile = compile_to_hardware(seqs, 'EchoCR/EchoCR', axis_descriptor=[ diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 4c53d381..901d20d1 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -19,6 +19,7 @@ import numpy as np import os import operator +import psutil from warnings import warn from copy import copy from functools import reduce @@ -317,10 +318,10 @@ def compile_to_qgl_IR(seqs, add_slave_trigger (optional): add the slave trigger(s) tdm_seq (optional): compile for TDM ''' - logger.debug("Compiling %d sequence(s)", len(seqs)) + process = psutil.Process(os.getpid()) - # save input code to file - save_code(seqs, fileName + suffix) + logger.info("Compiling %d sequence(s)", len(seqs)) + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) # all sequences should start with a WAIT for synchronization for seq in seqs: @@ -329,21 +330,25 @@ def compile_to_qgl_IR(seqs, seq.insert(0, ControlFlow.Wait()) # Add the digitizer trigger to measurements - logger.debug("Adding digitizer trigger") + logger.info("Adding digitizer trigger") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) PatternUtils.add_digitizer_trigger(seqs) # Add gating/blanking pulses - logger.debug("Adding blanking pulses") + logger.info("Adding blanking pulses") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) for seq in seqs: PatternUtils.add_gate_pulses(seq) if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: # Add the slave trigger - logger.debug("Adding slave trigger") + logger.info("Adding slave trigger") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) PatternUtils.add_slave_trigger(seqs, ChannelLibraries.channelLib['slave_trig']) else: - logger.debug("Not adding slave trigger") + logger.info("Not adding slave trigger") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) # find channel set at top level to account for individual sequence channel variability channels = set() @@ -351,14 +356,17 @@ def compile_to_qgl_IR(seqs, channels |= find_unique_channels(seq) # Compile all the pulses/pulseblocks to sequences of pulses and control flow + logger.info("Compiling sequences") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) wireSeqs = compile_sequences(seqs, channels) if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") return - logger.debug('') - logger.debug("Now after gating constraints:") + logger.info('') + logger.info("Now after gating constraints:") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) # apply gating constraints for chan, seq in wireSeqs.items(): if isinstance(chan, Channels.LogicalMarkerChannel): @@ -367,6 +375,8 @@ def compile_to_qgl_IR(seqs, debug_print(wireSeqs, 'Gated sequence') # save number of measurements for meta info + logger.info("Counting measurements") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) num_measurements = count_measurements(wireSeqs) wire_measurements = count_measurements_per_wire(wireSeqs) @@ -374,6 +384,8 @@ def compile_to_qgl_IR(seqs, # PhysicalQuadratureChannels and PhysicalMarkerChannels # for the APS, the naming convention is: # ASPName-12, or APSName-12m1 + logger.info("Mapping logical to physical") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) physWires = map_logical_to_physical(wireSeqs) # Pave the way for composite instruments, not useful yet... @@ -399,6 +411,8 @@ def compile_to_qgl_IR(seqs, files[inst_name] = {} # construct channel delay map + logger.info("Building delay map") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) delays = channel_delay_map(physWires) # apply delays @@ -407,15 +421,22 @@ def compile_to_qgl_IR(seqs, debug_print(physWires, 'Delayed wire') # generate wf library (base shapes) + logger.info("Generating waveforms") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) wfs = generate_waveforms(physWires) # replace Pulse objects with Waveforms + logger.info("Mapping pulses to waveforms") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) physWires = pulses_to_waveforms(physWires) # bundle wires on instruments, or channels depending # on whether we have one sequence per channel + logger.info("Bundling wires!") + logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) awgData = bundle_wires(physWires, wfs) + logger.info("DONE!") return awgData def compile_to_hardware(seqs, From 087bf2f1a177ade5cf5c7a8beeaf45c44de31d39 Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 11 Jan 2019 13:20:01 -0500 Subject: [PATCH 027/235] Memoize waveform objects to save on memory. --- QGL/Compiler.py | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 901d20d1..cd3030b3 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -22,10 +22,9 @@ import psutil from warnings import warn from copy import copy -from functools import reduce +from functools import reduce, lru_cache from importlib import import_module import json - from . import config from . import PatternUtils from .PatternUtils import flatten, has_gate @@ -388,6 +387,8 @@ def compile_to_qgl_IR(seqs, logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) physWires = map_logical_to_physical(wireSeqs) + del wireSeqs + # Pave the way for composite instruments, not useful yet... files = {} label_to_inst = {} @@ -437,6 +438,7 @@ def compile_to_qgl_IR(seqs, awgData = bundle_wires(physWires, wfs) logger.info("DONE!") + logger.info("Used {} GB of memory".format(process.memory_info().rss/1e9)) return awgData def compile_to_hardware(seqs, @@ -795,12 +797,24 @@ def normalize(seq, channels=None): block.pulses[ch] = Id(ch, blocklen) return seq -class Waveform(object): +class MemoizedObject(type): + """Metaclass for memoizing objects. Designed to save memory when processing + very long sequences. Overrides __call__ to prevent creating a new instance. + Idea from https://stackoverflow.com/questions/47785795/memoized-objects-still-have-their-init-invoked + """ + def __init__(self, name, bases, namespace): + super().__init__(name, bases, namespace) + self.cache = {} + def __call__(self, pulse=None): + hash = pulse.hashshape() + if pulse is not None and hash not in self.cache: + self.cache[hash] = super().__call__(pulse=pulse) + return self.cache[hash] + +class Waveform(metaclass=MemoizedObject): """ Simplified channel independent version of a Pulse with a key into waveform library. """ - - def __init__(self, pulse=None): if pulse is None: self.label = "" From 3f346dff67c036d13e7869a702609a25055e03f2 Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 11 Jan 2019 14:52:53 -0500 Subject: [PATCH 028/235] Remove debug stuff. --- QGL/Compiler.py | 147 +----------------------------------------------- 1 file changed, 2 insertions(+), 145 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index cd3030b3..61bc01d1 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -19,10 +19,9 @@ import numpy as np import os import operator -import psutil from warnings import warn from copy import copy -from functools import reduce, lru_cache +from functools import reduce from importlib import import_module import json from . import config @@ -299,148 +298,6 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_qgl_IR(seqs, - suffix='', - axis_descriptor=None, - add_slave_trigger=True, - extra_meta=None, - tdm_seq = False): - ''' - Compiles 'seqs' to a hardware description and saves it to 'fileName'. - Other inputs: - suffix (optional): string to append to end of fileName, e.g. with - fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 - axis_descriptor (optional): a list of dictionaries describing the effective - axes of the measurements that the sequence will yield. For instance, - if `seqs` generates a Ramsey experiment, axis_descriptor would describe - the time delays between pulses. - add_slave_trigger (optional): add the slave trigger(s) - tdm_seq (optional): compile for TDM - ''' - process = psutil.Process(os.getpid()) - - logger.info("Compiling %d sequence(s)", len(seqs)) - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - - # all sequences should start with a WAIT for synchronization - for seq in seqs: - if not isinstance(seq[0], ControlFlow.Wait): - logger.debug("Adding a WAIT - first sequence element was %s", seq[0]) - seq.insert(0, ControlFlow.Wait()) - - # Add the digitizer trigger to measurements - logger.info("Adding digitizer trigger") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - PatternUtils.add_digitizer_trigger(seqs) - - # Add gating/blanking pulses - logger.info("Adding blanking pulses") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - for seq in seqs: - PatternUtils.add_gate_pulses(seq) - - if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: - # Add the slave trigger - logger.info("Adding slave trigger") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - PatternUtils.add_slave_trigger(seqs, - ChannelLibraries.channelLib['slave_trig']) - else: - logger.info("Not adding slave trigger") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - - # find channel set at top level to account for individual sequence channel variability - channels = set() - for seq in seqs: - channels |= find_unique_channels(seq) - - # Compile all the pulses/pulseblocks to sequences of pulses and control flow - logger.info("Compiling sequences") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - wireSeqs = compile_sequences(seqs, channels) - - if not validate_linklist_channels(wireSeqs.keys()): - print("Compile to hardware failed") - return - - logger.info('') - logger.info("Now after gating constraints:") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - # apply gating constraints - for chan, seq in wireSeqs.items(): - if isinstance(chan, Channels.LogicalMarkerChannel): - wireSeqs[chan] = PatternUtils.apply_gating_constraints( - chan.phys_chan, seq) - debug_print(wireSeqs, 'Gated sequence') - - # save number of measurements for meta info - logger.info("Counting measurements") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - num_measurements = count_measurements(wireSeqs) - wire_measurements = count_measurements_per_wire(wireSeqs) - - # map logical to physical channels, physWires is a list of - # PhysicalQuadratureChannels and PhysicalMarkerChannels - # for the APS, the naming convention is: - # ASPName-12, or APSName-12m1 - logger.info("Mapping logical to physical") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - physWires = map_logical_to_physical(wireSeqs) - - del wireSeqs - - # Pave the way for composite instruments, not useful yet... - files = {} - label_to_inst = {} - label_to_chan = {} - old_wire_names = {} - old_wire_instrs = {} - for wire, pulses in physWires.items(): - pattern_module = import_module('QGL.drivers.' + wire.translator) - if pattern_module.SEQFILE_PER_CHANNEL: - inst_name = pattern_module.get_true_inst_name(wire.label) - chan_name = pattern_module.get_true_chan_name(wire.label) - has_non_id_pulses = any([len([p for p in ps if isinstance(p,Pulse) and p.label!="Id"]) > 0 for ps in pulses]) - label_to_inst[wire.label] = inst_name - if has_non_id_pulses: - label_to_chan[wire.label] = chan_name - # Change the name/inst for uniqueness, but we must restore this later! - old_wire_names[wire] = wire.label - old_wire_instrs[wire] = wire.instrument - wire.instrument = wire.label - wire.label = chan_name - files[inst_name] = {} - - # construct channel delay map - logger.info("Building delay map") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - delays = channel_delay_map(physWires) - - # apply delays - for chan, wire in physWires.items(): - PatternUtils.delay(wire, delays[chan]) - debug_print(physWires, 'Delayed wire') - - # generate wf library (base shapes) - logger.info("Generating waveforms") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - wfs = generate_waveforms(physWires) - - # replace Pulse objects with Waveforms - logger.info("Mapping pulses to waveforms") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - physWires = pulses_to_waveforms(physWires) - - # bundle wires on instruments, or channels depending - # on whether we have one sequence per channel - logger.info("Bundling wires!") - logger.info("Using {} GB of memory".format(process.memory_info().rss/1e9)) - awgData = bundle_wires(physWires, wfs) - - logger.info("DONE!") - logger.info("Used {} GB of memory".format(process.memory_info().rss/1e9)) - return awgData - def compile_to_hardware(seqs, fileName, suffix='', @@ -963,7 +820,7 @@ def count_measurements_per_wire(wireSeqs): seq_len = len(wireSeqs[list(wireSeqs)[0]]) meas_wires = list(filter(lambda x: isinstance(x, Channels.Measurement), wireSeqs)) measurements = {wire: 0 for wire in meas_wires} - for ct in range(seq_len): + for ct in range(seq_lendef seq_measurements = count_measurements_per_wire_idx(wireSeqs, ct) for wire in meas_wires: measurements[wire] += seq_measurements[wire] From 9fc12f213f49822e2e05fce42b4ff1e6a61fd53f Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 11 Jan 2019 16:28:33 -0500 Subject: [PATCH 029/235] More typos. --- QGL/Compiler.py | 2 +- tests/test_perf.py | 19 ++++++++++--------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 61bc01d1..40ac4fb0 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -820,7 +820,7 @@ def count_measurements_per_wire(wireSeqs): seq_len = len(wireSeqs[list(wireSeqs)[0]]) meas_wires = list(filter(lambda x: isinstance(x, Channels.Measurement), wireSeqs)) measurements = {wire: 0 for wire in meas_wires} - for ct in range(seq_lendef + for ct in range(seq_len): seq_measurements = count_measurements_per_wire_idx(wireSeqs, ct) for wire in meas_wires: measurements[wire] += seq_measurements[wire] diff --git a/tests/test_perf.py b/tests/test_perf.py index aea99755..1830e1e2 100644 --- a/tests/test_perf.py +++ b/tests/test_perf.py @@ -3,16 +3,17 @@ from QGL import * import random -N = 1000 +def random_sequence_test(): + N = 1000 -cl = ChannelLibrary() -q1 = QubitFactory("q1") + cl = ChannelLibrary() + q1 = QubitFactory("q1") -gates = (X90(q1), Y90(q1), X(q1), Y(q1), Id(q1, length=1e-7)) + gates = (X90(q1), Y90(q1), X(q1), Y(q1), Id(q1, length=1e-7)) -seqs = [] -ks = random.choices(range(1,100), k = N) -for k in ks: - seqs.append(random.choices(gates, k=k) + [MEAS(q1)]) + seqs = [] + ks = random.choices(range(1,100), k = N) + for k in ks: + seqs.append(random.choices(gates, k=k) + [MEAS(q1)]) -compile_to_hardware(seqs, 'test/test') + compile_to_hardware(seqs, 'test/test') From 2d26637a2846ce92b28c29c6550c8868ebb56af2 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 14 Jan 2019 15:27:53 -0500 Subject: [PATCH 030/235] Use __slots__ for Waveform class to save on memory... --- QGL/Compiler.py | 76 +++++++++++++++++++++++++++++++++++++++---- QGL/PulseSequencer.py | 6 ++++ 2 files changed, 76 insertions(+), 6 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 40ac4fb0..312176bf 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -36,6 +36,8 @@ from . import BlockLabel from . import TdmInstructions # only for APS2-TDM +import psutil + logger = logging.getLogger(__name__) def map_logical_to_physical(wires): @@ -298,6 +300,9 @@ def collect_specializations(seqs): done.append(target) return funcs +def _get_mem(): + return psutil.Process(os.getpid()).memory_info().rss + def compile_to_hardware(seqs, fileName, suffix='', @@ -322,6 +327,8 @@ def compile_to_hardware(seqs, # save input code to file save_code(seqs, fileName + suffix) + mem = _get_mem() + # all sequences should start with a WAIT for synchronization for seq in seqs: if not isinstance(seq[0], ControlFlow.Wait): @@ -329,13 +336,19 @@ def compile_to_hardware(seqs, seq.insert(0, ControlFlow.Wait()) # Add the digitizer trigger to measurements - logger.debug("Adding digitizer trigger") + logger.info("Adding digitizer trigger") PatternUtils.add_digitizer_trigger(seqs) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # Add gating/blanking pulses - logger.debug("Adding blanking pulses") + logger.info("Adding blanking pulses") for seq in seqs: PatternUtils.add_gate_pulses(seq) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: # Add the slave trigger @@ -343,15 +356,23 @@ def compile_to_hardware(seqs, PatternUtils.add_slave_trigger(seqs, ChannelLibraries.channelLib['slave_trig']) else: - logger.debug("Not adding slave trigger") + logger.info("Not adding slave trigger") # find channel set at top level to account for individual sequence channel variability + logger.info("Finding unique channels.") channels = set() for seq in seqs: channels |= find_unique_channels(seq) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # Compile all the pulses/pulseblocks to sequences of pulses and control flow + logger.info("Compiling sequences.") wireSeqs = compile_sequences(seqs, channels) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") @@ -360,21 +381,33 @@ def compile_to_hardware(seqs, logger.debug('') logger.debug("Now after gating constraints:") # apply gating constraints + logger.info("Applying gating constraints") for chan, seq in wireSeqs.items(): if isinstance(chan, Channels.LogicalMarkerChannel): wireSeqs[chan] = PatternUtils.apply_gating_constraints( chan.phys_chan, seq) debug_print(wireSeqs, 'Gated sequence') + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # save number of measurements for meta info + logger.info("Counting measurements.") num_measurements = count_measurements(wireSeqs) wire_measurements = count_measurements_per_wire(wireSeqs) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # map logical to physical channels, physWires is a list of # PhysicalQuadratureChannels and PhysicalMarkerChannels # for the APS, the naming convention is: # ASPName-12, or APSName-12m1 + logger.info("Mapping logical to physical channels.") physWires = map_logical_to_physical(wireSeqs) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # Pave the way for composite instruments, not useful yet... files = {} @@ -399,22 +432,43 @@ def compile_to_hardware(seqs, files[inst_name] = {} # construct channel delay map + logger.info("Constructing delay map.") delays = channel_delay_map(physWires) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # apply delays + logger.info("Applying delays.") for chan, wire in physWires.items(): PatternUtils.delay(wire, delays[chan]) debug_print(physWires, 'Delayed wire') + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # generate wf library (base shapes) + logger.info("Generating waveform library.") wfs = generate_waveforms(physWires) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # replace Pulse objects with Waveforms + logger.info("Replacing pulses with waveforms") physWires = pulses_to_waveforms(physWires) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem # bundle wires on instruments, or channels depending # on whether we have one sequence per channel + logger.info("Bundling wires.") awgData = bundle_wires(physWires, wfs) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem + # convert to hardware formats # files = {} @@ -428,7 +482,11 @@ def compile_to_hardware(seqs, fullFileName = os.path.normpath(os.path.join( config.AWGDir, fileName + '-' + awgName + suffix + data[ 'seqFileExt'])) + logger.info("Writing sequence file for: {}".format(awgName)) new_meta = data['translator'].write_sequence_file(data, fullFileName) + new_mem = _get_mem() + logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) + mem = new_mem if new_meta: awg_metas[awgName] = new_meta @@ -668,10 +726,15 @@ def __call__(self, pulse=None): self.cache[hash] = super().__call__(pulse=pulse) return self.cache[hash] -class Waveform(metaclass=MemoizedObject): +class Waveform(object): """ Simplified channel independent version of a Pulse with a key into waveform library. """ + + #Use slots to create attributes to save on memory. + __slots__ = ["label", "key", "amp", "length", "phase", "frameChange", + "isTimeAmp", "frequency", "logicalChan", "maddr", "startTime"] + def __init__(self, pulse=None): if pulse is None: self.label = "" @@ -709,14 +772,15 @@ def __str__(self): def __eq__(self, other): if isinstance(other, self.__class__): - return self.__dict__ == other.__dict__ + #No __dict__ property so we check all properties. + return all((getattr(self, attr, None) == getattr(other, attr, None) for attr in self.__slots__)) return False def __ne__(self, other): return not self == other def __hash__(self): - return hash(frozenset(self.__dict__.items())) + return hash(frozenset((attr, getattr(self, attr, None)) for attr in self.__slots__)) @property def isZero(self): diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 0d9e78d0..9f4d6913 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -77,6 +77,12 @@ def _repr_pretty_(self, p, cycle): def hashshape(self): return hash(frozenset(self.shapeParams.items())) + def __hash__(self): + d = self._asdict() + d['shapeParams'] = self.hashshape() + del d['ignoredStrParams'] + return hash(frozenset(d.items())) + def __add__(self, other): if self.channel != other.channel: raise NameError( From b39125c59bc81440214b810ee05df5431b3972c5 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 24 Jan 2019 15:19:49 -0500 Subject: [PATCH 031/235] Attempt at manual garbage collection. --- QGL/Compiler.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 312176bf..5c3c1a9e 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -35,6 +35,7 @@ from . import ControlFlow from . import BlockLabel from . import TdmInstructions # only for APS2-TDM +import gc import psutil @@ -469,11 +470,14 @@ def compile_to_hardware(seqs, logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) mem = new_mem + del wireSeqs + gc.collect() # convert to hardware formats # files = {} awg_metas = {} - for awgName, data in awgData.items(): + for awgName in list(awgData.keys()): + data = awgData[awgName] # create the target folder if it does not exist targetFolder = os.path.split(os.path.normpath(os.path.join( config.AWGDir, fileName)))[0] @@ -496,6 +500,11 @@ def compile_to_hardware(seqs, files[label_to_inst[awgName]][label_to_chan[awgName]] = fullFileName else: files[awgName] = fullFileName + + del data + del awgData[awgName] + gc.collect() + # generate TDM sequences FIXME: what's the best way to identify the need for a TDM seq.? Support for single TDM if tdm_seq and 'APS2Pattern' in [wire.translator for wire in physWires]: aps2tdm_module = import_module('QGL.drivers.APS2Pattern') # this is redundant with above From 1cac1609514776a1f234eac66f674ef501bc6234 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 28 Jan 2019 15:30:36 -0500 Subject: [PATCH 032/235] Remove memory debug info --- QGL/Compiler.py | 47 ----------------------------------------------- 1 file changed, 47 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 5c3c1a9e..97bf28ae 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -37,8 +37,6 @@ from . import TdmInstructions # only for APS2-TDM import gc -import psutil - logger = logging.getLogger(__name__) def map_logical_to_physical(wires): @@ -301,9 +299,6 @@ def collect_specializations(seqs): done.append(target) return funcs -def _get_mem(): - return psutil.Process(os.getpid()).memory_info().rss - def compile_to_hardware(seqs, fileName, suffix='', @@ -328,8 +323,6 @@ def compile_to_hardware(seqs, # save input code to file save_code(seqs, fileName + suffix) - mem = _get_mem() - # all sequences should start with a WAIT for synchronization for seq in seqs: if not isinstance(seq[0], ControlFlow.Wait): @@ -339,17 +332,11 @@ def compile_to_hardware(seqs, # Add the digitizer trigger to measurements logger.info("Adding digitizer trigger") PatternUtils.add_digitizer_trigger(seqs) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # Add gating/blanking pulses logger.info("Adding blanking pulses") for seq in seqs: PatternUtils.add_gate_pulses(seq) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: # Add the slave trigger @@ -364,16 +351,10 @@ def compile_to_hardware(seqs, channels = set() for seq in seqs: channels |= find_unique_channels(seq) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # Compile all the pulses/pulseblocks to sequences of pulses and control flow logger.info("Compiling sequences.") wireSeqs = compile_sequences(seqs, channels) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") @@ -388,17 +369,11 @@ def compile_to_hardware(seqs, wireSeqs[chan] = PatternUtils.apply_gating_constraints( chan.phys_chan, seq) debug_print(wireSeqs, 'Gated sequence') - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # save number of measurements for meta info logger.info("Counting measurements.") num_measurements = count_measurements(wireSeqs) wire_measurements = count_measurements_per_wire(wireSeqs) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # map logical to physical channels, physWires is a list of # PhysicalQuadratureChannels and PhysicalMarkerChannels @@ -406,9 +381,6 @@ def compile_to_hardware(seqs, # ASPName-12, or APSName-12m1 logger.info("Mapping logical to physical channels.") physWires = map_logical_to_physical(wireSeqs) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # Pave the way for composite instruments, not useful yet... files = {} @@ -435,41 +407,25 @@ def compile_to_hardware(seqs, # construct channel delay map logger.info("Constructing delay map.") delays = channel_delay_map(physWires) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # apply delays logger.info("Applying delays.") for chan, wire in physWires.items(): PatternUtils.delay(wire, delays[chan]) debug_print(physWires, 'Delayed wire') - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # generate wf library (base shapes) logger.info("Generating waveform library.") wfs = generate_waveforms(physWires) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") physWires = pulses_to_waveforms(physWires) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem # bundle wires on instruments, or channels depending # on whether we have one sequence per channel logger.info("Bundling wires.") awgData = bundle_wires(physWires, wfs) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem - del wireSeqs gc.collect() @@ -488,9 +444,6 @@ def compile_to_hardware(seqs, 'seqFileExt'])) logger.info("Writing sequence file for: {}".format(awgName)) new_meta = data['translator'].write_sequence_file(data, fullFileName) - new_mem = _get_mem() - logger.info("Used {} MB of memory.".format((new_mem - mem)/1e6)) - mem = new_mem if new_meta: awg_metas[awgName] = new_meta From f32df2ca7f564848ba6b1b2b6689aae8e32493a3 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 28 Jan 2019 15:44:23 -0500 Subject: [PATCH 033/235] Skip GSTTools unit tests if running in Travis. --- tests/test_Sequences.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index ada1e278..1e145046 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -15,6 +15,11 @@ import logging logger = logging.getLogger( 'sequences') +import os +istravis = os.environ.get('TRAVIS') == 'true' + +#Determine if tests are running in Travis + class AWGTestHelper(object): testFileDirectory = './tests/test_data/awg/' @@ -386,6 +391,10 @@ def test_RB_SimultaneousRB_AC(self): self.compare_sequences('RB') def test_1Q_GST(self): + + if istravis: + raise unittest.SkipTest("FIX ME -- Figure out pygsti integration for Travis.") + self.set_awg_dir('GST') # list of GST gate strings if GSTTools.PYGSTI_PRESENT: @@ -408,6 +417,10 @@ def test_1Q_GST(self): self.compare_sequences('GST') def test_2Q_GST(self): + + if istravis: + raise unittest.SkipTest("FIX ME -- Figure out pygsti integration for Travis.") + self.set_awg_dir('GST') def gst_2Qgate_map(q1, q2): return {"Gxi": X90(q1)*Id(q2), From cbeb82a272043e3a047758ac983345c3f0a91a83 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 28 Jan 2019 15:58:17 -0500 Subject: [PATCH 034/235] Fix deprecation warning for empty array check --- QGL/Compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 97bf28ae..42cfd7fe 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -101,7 +101,7 @@ def merge_channels(wires, channels): [e.amp * e.frequency for e in entries])[0] assert len(nonZeroSSBChan) <= 1, \ "Unable to handle merging more than one non-zero entry with non-zero frequency." - if nonZeroSSBChan: + if nonZeroSSBChan.size > 0: frequency = entries[nonZeroSSBChan[0]].frequency else: frequency = 0.0 From 575239e588e138411a9ab3739e203abc1f0876e2 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 28 Jan 2019 15:58:29 -0500 Subject: [PATCH 035/235] Get rid of 3.5 CI testing. --- .travis.yml | 1 - 1 file changed, 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index a76dc173..d936dc77 100644 --- a/.travis.yml +++ b/.travis.yml @@ -3,7 +3,6 @@ language: python python: - - 3.5 - 3.6 before_install: From 6af2cf2e88f20aa15093398535bf819e6845cf24 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 23 May 2018 11:56:49 -0400 Subject: [PATCH 036/235] Autogen stream selectors --- QGL/ChannelLibraries.py | 52 ++++++++++++++++++++++++----------------- 1 file changed, 31 insertions(+), 21 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 6afb03b2..caac68e5 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -204,13 +204,13 @@ def load_from_library(self, return_only=False): loader.dispose() # Check to see if we have the mandatory sections - for section in ['instruments', 'qubits', 'filters']: + for section in ['instruments', 'qubits']: #, 'filters']: if section not in tmpLib.keys(): raise ValueError("{} section not present in config file {}.".format(section, self.library_file)) instr_dict = tmpLib['instruments'] qubit_dict = tmpLib['qubits'] - filter_dict = tmpLib['filters'] + # filter_dict = tmpLib['filters'] trigger_dict = tmpLib.get('markers', {}) # This section is optional edge_dict = tmpLib.get('edges', {}) # This section is optional master_awgs = [] @@ -295,23 +295,33 @@ def load_from_library(self, return_only=False): # params["__class__"] = "LogicalMarkerChannel" # channel_dict[params["label"]] = params - for name, filt in filter_dict.items(): - if "StreamSelector" in filt["type"]: - params = {k: v for k,v in filt.items() if k in Channels.ReceiverChannel.__atom_members__.keys()} - params["label"] = "RecvChan-" + name # instr_dict[filt["instrument"]]["name"] + "-" + name - params["channel"] = str(params["channel"]) # Convert to a string - params["instrument"] = filt["source"] - params["__module__"] = "QGL.Channels" - params["__class__"] = "ReceiverChannel" - if "source" not in filt.keys(): - raise ValueError("No instrument (source) specified for Stream Selector") - if filt["source"] not in instr_dict.keys() and filt["source"] not in channel_dict.keys(): - raise ValueError("Stream Selector source {} not found among list of instruments.".format(filt["source"])) - params["instrument"] = filt["source"] - - channel_dict[params["label"]] = params + # for name, filt in filter_dict.items(): + # if "StreamSelector" in filt["type"]: + # params = {k: v for k,v in filt.items() if k in Channels.ReceiverChannel.__atom_members__.keys()} + # params["label"] = "RecvChan-" + name # instr_dict[filt["instrument"]]["name"] + "-" + name + # params["channel"] = str(params["channel"]) # Convert to a string + # params["instrument"] = filt["source"] + # params["__module__"] = "QGL.Channels" + # params["__class__"] = "ReceiverChannel" + # if "source" not in filt.keys(): + # raise ValueError("No instrument (source) specified for Stream Selector") + # if filt["source"] not in instr_dict.keys() and filt["source"] not in channel_dict.keys(): + # raise ValueError("Stream Selector source {} not found among list of instruments.".format(filt["source"])) + # params["instrument"] = filt["source"] + + # channel_dict[params["label"]] = params for name, qubit in qubit_dict.items(): + # Create a stream selector + rcv_inst, rcv_chan, rcv_stream = qubit["measure"]["receiver"].split() + rcv_params = {} + rcv_params["label"] = "RecvChan-" + name + "-SS" + rcv_params["channel"] = str(rcv_chan) + rcv_params["instrument"] = rcv_inst + rcv_params["__module__"] = "QGL.Channels" + rcv_params["__class__"] = "ReceiverChannel" + channel_dict[rcv_params["label"]] = rcv_params + # Create the Qubits if len(qubit["control"]["AWG"].split()) != 2: print("Control AWG specification for {} ({}) must have a device, channel".format(name, qubit["control"]["AWG"])) @@ -319,7 +329,7 @@ def load_from_library(self, return_only=False): ctrl_instr, ctrl_chan = qubit["control"]["AWG"].split() params = {k: v for k,v in qubit["control"].items() if k in Channels.Qubit.__atom_members__.keys()} params["label"] = name - params["phys_chan"] = ctrl_instr + "-" + ctrl_chan + params["phys_chan"] = ctrl_instr + "-" + ctrl_chan params["__module__"] = "QGL.Channels" params["__class__"] = "Qubit" channel_dict[params["label"]] = params @@ -338,7 +348,7 @@ def load_from_library(self, return_only=False): params["trig_chan"] = "digTrig-" + dig_trig params["phys_chan"] = meas_instr + "-" + meas_chan params["meas_type"] = "autodyne" - params["receiver_chan"] = "RecvChan-" + qubit["measure"]["receiver"] + params["receiver_chan"] = rcv_params["label"] params["__module__"] = "QGL.Channels" params["__class__"] = "Measurement" channel_dict[params["label"]] = params @@ -347,8 +357,8 @@ def load_from_library(self, return_only=False): # Create the receiver channels if "receiver" in qubit["measure"].keys(): - if len(qubit["measure"]["receiver"].split()) != 1: - print("Receiver specification for {} ({}) must have a stream selector".format(name, qubit["measure"]["receiver"])) + if len(qubit["measure"]["receiver"].split()) != 3: + print("Receiver specification for {} ({}) must have an instrument name, physical channel, and stream".format(name, qubit["measure"]["receiver"])) raise ValueError("Receiver specification for {} ({}) must have a stream selector".format(name, qubit["measure"]["receiver"])) phys_instr, phys_marker = dig_trig.split() params = {} From c3055e4cb7d9b627f362a42f0db0b2ef475ff8c1 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 31 May 2018 16:41:16 -0400 Subject: [PATCH 037/235] Ditch atom, move to Pony.orm for all channel library objects. --- QGL/ChannelLibraries.py | 327 +++++----------------------------------- QGL/Channels.py | 214 +++++++++++--------------- QGL/Compiler.py | 2 + QGL/PulseSequencer.py | 5 +- QGL/PulseShapes.py | 5 +- QGL/config.py | 12 +- 6 files changed, 142 insertions(+), 423 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index caac68e5..1078716d 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -33,7 +33,7 @@ import traceback import datetime import importlib -from atom.api import Atom, Str, Int, Typed +from pony.orm import * import networkx as nx import yaml @@ -46,118 +46,37 @@ from watchdog.events import FileSystemEventHandler import time +from . import config from . import Channels from . import PulseShapes -from . import config channelLib = None -class MyEventHandler(FileSystemEventHandler): - - def __init__(self, file_paths, callback): - super(MyEventHandler, self).__init__() - self.file_paths = [os.path.normpath(fn) for fn in file_paths] - self.callback = callback - self.paused = True - - # The spotlight indexer in MacOSX retriggers events... maybe we should hash the files? - self.grace_period = 3.0 if sys.platform == 'darwin' else 1.0 - self.last_library_update = datetime.datetime.now() - - def on_modified(self, event): - try: - if any([os.path.samefile(event.src_path, fp) for fp in self.file_paths]): - if not self.paused: - # Build in some sanity checking since we seem to get multiple - # events firing in a number of situations. - now = datetime.datetime.now() - - if (now-self.last_library_update).total_seconds() > (self.grace_period): - self.last_library_update = now - """ - Hold off for half a second - If the event is from the file being opened to be written this gives - time for it to be written. - """ - time.sleep(0.5) - self.callback() - except FileNotFoundError: - #Temporary settings files generated using yaml_dump get deleted - #faster than the above code can catch it. - pass - -class LibraryFileWatcher(object): - def __init__(self, main_path, callback): - super(LibraryFileWatcher, self).__init__() - - self.main_path = os.path.normpath(main_path) - self.callback = callback - - # Perform a preliminary loading to find all of the connected files... - # TODO: modularity - with open(os.path.abspath(self.main_path), 'r') as FID: - loader = config.Loader(FID) +def set_from_dict(obj, settings): + for prop_name in obj.to_dict().keys(): + if prop_name in settings.keys(): try: - tmpLib = loader.get_single_data() - self.filenames = [os.path.normpath(lf) for lf in loader.filenames] - finally: - loader.dispose() - - self.eventHandler = MyEventHandler(self.filenames, self.callback) - self.observer = Observer() - self.observer.schedule(self.eventHandler, path=os.path.dirname(os.path.abspath(main_path))) - - self.observer.start() - self.resume() - - def __del__(self): - self.observer.stop() - self.observer.join() - - def pause(self): - self.eventHandler.paused = True + setattr(obj, prop_name, settings[prop_name]) + except Exception as e: + print(f"{obj.label}: Error loading {prop_name} from config") - def resume(self): - self.eventHandler.paused = False - -class ChannelLibrary(Atom): - # channelDict = Dict(Str, Channel) - channelDict = Typed(dict) - connectivityG = Typed(nx.DiGraph) - library_file = Str() - fileWatcher = Typed(LibraryFileWatcher) - version = Int(5) - last_library_update = Str() - - specialParams = ['phys_chan', 'gate_chan', 'trig_chan', 'receiver_chan', - 'source', 'target'] +class ChannelLibrary(object): def __init__(self, library_file=None, blank=False, channelDict={}, **kwargs): """Create the channel library. We assume that the user wants the config file in the usual locations specified in the config files.""" - + # Load the basic config options from the yaml self.library_file = config.load_config(library_file) - if blank: # we want a blank library if library_file is none - super(ChannelLibrary, self).__init__(channelDict={}) - self.connectivityG = nx.DiGraph() - else: - super(ChannelLibrary, self).__init__(channelDict=channelDict, library_file=self.library_file, **kwargs) - self.connectivityG = nx.DiGraph() - yaml_filenames = self.load_from_library() - if self.library_file and yaml_filenames: - self.fileWatcher = LibraryFileWatcher(self.library_file, self.update_from_file) + self.connectivityG = nx.DiGraph() + + self.channelDict = {c.label: c for c in select(c for c in Channels.Channel)} # Update the global reference global channelLib - if channelLib: - # Don't let the - channelLib.fileWatcher = None channelLib = self - self.last_library_update = str(datetime.datetime.now()) - #Dictionary methods def __getitem__(self, key): return self.channelDict[key] @@ -179,6 +98,7 @@ def values(self): def build_connectivity_graph(self): # build connectivity graph +<<<<<<< HEAD self.connectivityG.clear() for chan in self.channelDict.values(): if isinstance(chan, @@ -374,210 +294,40 @@ def load_from_library(self, return_only=False): # Don't duplicate triggers to the same digitizer if params["label"] not in channel_dict.keys(): channel_dict[params["label"]] = params +======= + for chan in select(q for q in Channels.Qubit if q not in self.connectivityG): + self.connectivityG.add_node(chan) + for chan in select(e for e in Channels.Edge): + self.connectivityG.add_edge(chan.source, chan.target) + self.connectivityG[chan.source][chan.target]['channel'] = chan +>>>>>>> Ditch atom, move to Pony.orm for all channel library objects. - # Create the measurement gate chan: - if "gate" in qubit["measure"].keys(): - phys_instr, phys_marker = qubit["measure"]["gate"].split() - params = {} - params["label"] = "M-{}-gate".format(name) - params["phys_chan"] = phys_instr + "-" + phys_marker - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - channel_dict[params["label"]] = params - channel_dict["M-{}".format(name)]["gate_chan"] = params["label"] - - # Create the control gate chan: - if "gate" in qubit["control"].keys(): - phys_instr, phys_marker = qubit["control"]["gate"].split() - params = {} - params["label"] = "{}-gate".format(name) - params["phys_chan"] = phys_instr + "-" + phys_marker - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - channel_dict[params["label"]] = params - channel_dict[name]["gate_chan"] = params["label"] - - - for trig_name, trigger in trigger_dict.items(): - phys_instr, phys_marker = trigger.split() - params = {} - params["label"] = trig_name - params["phys_chan"] = phys_instr + "-" + phys_marker - if params["phys_chan"] in marker_lens.keys(): - length = marker_lens[params["phys_chan"]] - else: - length = 1e-7 - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - channel_dict[params["label"]] = params - - for name, edge in edge_dict.items(): - # Create the Edges - if len(edge["AWG"].split()) != 2: - print("Control AWG specification for {} ({}) must have a device, channel".format(name, edge["AWG"])) - raise ValueError("Control AWG specification for {} ({}) must have a device, channel".format(name, edge["AWG"])) - ctrl_instr, ctrl_chan = edge["AWG"].split() - params = {k: v for k,v in edge.items() if k in Channels.Edge.__atom_members__.keys()} - params["label"] = name - params["phys_chan"] = ctrl_instr + "-" + ctrl_chan - params["__module__"] = "QGL.Channels" - params["__class__"] = "Edge" - channel_dict[params["label"]] = params - if 'generator' in edge.keys(): - channel_dict[params["phys_chan"]]["generator"] = edge["generator"] - - # Create the edge gate chan: - if "gate" in edge.keys(): - phys_instr, phys_marker = edge["gate"].split() - params = {} - params["label"] = "{}-gate".format(name) - params["phys_chan"] = phys_instr + "-" + phys_marker - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - channel_dict[params["label"]] = params - channel_dict[name]["gate_chan"] = params["label"] - - if return_only: - return channel_dict - else: - channel_dict = {k: self.instantiate(v) for k,v in channel_dict.items()} - # connect objects labeled by strings - for chan in channel_dict.values(): - for param in self.specialParams: - if hasattr(chan, param) and getattr(chan, param) is not None: - chan_to_find = channel_dict.get(getattr(chan, param), None) - if not chan_to_find: - print("Couldn't find {} of {} in the channel_dict!".format(param, chan)) - setattr(chan, param, chan_to_find) - - self.channelDict.update(channel_dict) - self.build_connectivity_graph() - return filenames - - except IOError: - print('No channel library found.') - except Exception as e: - print('Failed to load channel library: received exception', e) - exc_type, exc_value, exc_traceback = sys.exc_info() - traceback.print_tb(exc_traceback, limit=4, file=sys.stdout) - - def instantiate(self, paramDict): - if 'pulse_params' in paramDict: - if 'shape_fun' in paramDict['pulse_params']: - shape_fun = paramDict['pulse_params']['shape_fun'] - paramDict['pulse_params']['shape_fun'] = getattr(PulseShapes, shape_fun) - if '__class__' in paramDict: - className = paramDict.pop('__class__') - moduleName = paramDict.pop('__module__') - __import__(moduleName) - return getattr(sys.modules[moduleName], className)(**paramDict) - - - def update_from_file(self): - """ - Only update relevant parameters - Helps avoid both stale references from replacing whole channel objects (as in load_from_library) - and the overhead of recreating everything. - """ - - if not self.library_file: - return - try: - all_params = self.load_from_library(return_only=True) - - # update & insert - for chName, chParams in all_params.items(): - if chName in self.channelDict: - self.update_from_json(chName, chParams) - else: - self.channelDict[chName] = self.instantiate(chParams) - self.update_from_json(chName, chParams) - - # remove - for chName in list(self.channelDict.keys()): - if chName not in all_params: - del self.channelDict[chName] - - self.build_connectivity_graph() - - print("Updated library") - self.last_library_update = str(datetime.datetime.now()) - except: - print('Failed to update channel library from file. Is there a typo?.') - return - - # reset pulse cache - from . import PulsePrimitives - PulsePrimitives._memoize.cache.clear() - - - def update_from_json(self, chName, chParams): - # connect objects labeled by strings - if 'pulse_params' in chParams.keys(): - paramDict = {str(k): v for k, v in chParams['pulse_params'].items()} - shapeFunName = paramDict.pop('shape_fun', None) - if shapeFunName: - paramDict['shape_fun'] = getattr(PulseShapes, shapeFunName) - self.channelDict[chName].pulse_params = paramDict - - for param in self.specialParams: - if param in chParams.keys(): - setattr(self.channelDict[chName], - param, - self.channelDict.get(chParams[param], None) - ) - # TODO: how do we follow changes to selected AWG or generator? - - # ignored or specially handled parameters - ignoreList = self.specialParams + ['pulse_params', 'AWG', 'generator', '__class__', '__module__'] - for paramName in chParams: - if paramName not in ignoreList: - setattr(self.channelDict[chName], paramName, chParams[paramName]) - - def on_awg_change(self, oldName, newName): - print("Change AWG", oldName, newName) - for chName in self.channelDict: - if isinstance(self.channelDict[chName], - (Channels.PhysicalMarkerChannel, - Channels.PhysicalQuadratureChannel)): - awgName, awgChannel = chName.rsplit('-', 1) - if awgName == oldName: - newLabel = "{0}-{1}".format(newName, awgChannel) - print("Changing {0} to {1}".format(chName, newLabel)) - self.physicalChannelManager.name_changed(chName, newLabel) - -def MarkerFactory(label, **kwargs): - '''Return a marker channel by name. Must be defined under top-level `markers` - keyword in measurement configuration YAML. - ''' - if not channelLib: - raise ValueError('ChannelLibrary not found, has an instance of ChannelLibrary been created?') - if label in channelLib and isinstance(channelLib[label], Channels.LogicalMarkerChannel): - return channelLib[label] - else: - raise ValueError("Marker channel {} not found in channel library.".format(label)) def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' - if not channelLib: - raise ValueError('ChannelLibrary not found, has an instance of ChannelLibrary been created?') - if label in channelLib and isinstance(channelLib[label], Channels.Qubit): - return channelLib[label] + thing = select(el for el in Channels.Qubit if el.label==label).first() + if thing: + return thing else: return Channels.Qubit(label=label, **kwargs) - -def MeasFactory(label, meas_type='autodyne', **kwargs): + +def MeasFactory(label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' - if not channelLib: - raise ValueError('ChannelLibrary not found, has an instance of ChannelLibrary been created?') - if label in channelLib and isinstance(channelLib[label], Channels.Measurement): - return channelLib[label] + thing = select(el for el in Channels.Measurement if el.label==label).first() + if thing: + return thing else: - return Channels.Measurement(label=label, meas_type=meas_type, **kwargs) + return Channels.Measurement(label=label, **kwargs) + +def MarkerFactory(label, **kwargs): + ''' Return a saved Marker channel or create a new one. ''' + thing = select(el for el in Channels.LogicalMarkerChannel if el.label==label).first() + if thing: + return thing + else: + return Channels.LogicalMarkerChannel(label=label, **kwargs) def EdgeFactory(source, target): - if not channelLib: - raise ValueError('Connectivity graph not found. Has a ChannelLibrary has been created?') if channelLib.connectivityG.has_edge(source, target): return channelLib.connectivityG[source][target]['channel'] elif channelLib.connectivityG.has_edge(target, source): @@ -585,3 +335,4 @@ def EdgeFactory(source, target): else: raise ValueError('Edge {0} not found in connectivity graph'.format(( source, target))) + diff --git a/QGL/Channels.py b/QGL/Channels.py index c0c85163..3a9b7b8b 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -4,6 +4,7 @@ Created on Jan 19, 2012 Original Author: Colm Ryan +Modified By: Graham Rowlands Copyright 2013 Raytheon BBN Technologies @@ -20,162 +21,137 @@ limitations under the License. ''' +from . import config from . import PulseShapes import numpy as np - from math import tan, cos, pi -# Python 2/3 compatibility: use the py3 meaning of 'str' -from builtins import str - -from atom.api import Atom, Str, Float, Instance, \ - Dict, Enum, Bool, Typed, Int - +from pony.orm import * from copy import deepcopy -class Channel(Atom): +class Channel(config.db.Entity): ''' Every channel has a label and some printers. ''' - label = Str() - enabled = Bool(True) + label = Required(str) def __repr__(self): return str(self) - def __str__(self): return "{0}('{1}')".format(self.__class__.__name__, self.label) - def json_encode(self): - jsonDict = self.__getstate__() - - #Turn objects into labels - for member in ["phys_chan", "gate_chan", "trig_chan", "receiver_chan", "source", "target"]: - if member in jsonDict and not isinstance(jsonDict[member], str): - obj = jsonDict.pop(member) - if obj: - jsonDict[member] = obj.label - - #We want the name of shape functions - if "pulse_params" in jsonDict: - pulse_params = deepcopy(jsonDict.pop("pulse_params")) - if "shape_fun" in pulse_params: - pulse_params["shape_fun"] = pulse_params["shape_fun"].__name__ - jsonDict["pulse_params"] = pulse_params - - return jsonDict - - class PhysicalChannel(Channel): ''' The main class for actual AWG channels. ''' - instrument = Str() # i.e. the AWG or receiver - translator = Str() - generator = Str() - sampling_rate = Float(default=1.2e9) - delay = Float() + instrument = Optional(str) # i.e. the AWG or receiver + translator = Optional(str) + generator = Optional(str) + sampling_rate = Optional(float, default=1.2e9) + delay = Required(float, default=0.0) + # Required reverse connections + logical_channel = Optional("LogicalChannel") + quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") + quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") + marker_channel = Optional("PhysicalMarkerChannel") class LogicalChannel(Channel): ''' The main class from which we will generate sequences. At some point it needs to be assigned to a physical channel. + frequency: modulation frequency of the channel (can be positive or negative) ''' #During initilization we may just have a string reference to the channel - phys_chan = Instance((str, PhysicalChannel)) - - def __init__(self, **kwargs): - super(LogicalChannel, self).__init__(**kwargs) - if self.phys_chan is None: - self.phys_chan = PhysicalChannel(label=kwargs['label'] + '-phys') - + phys_chan = Optional(PhysicalChannel) + frequency = Required(float, default=0.0) + pulse_params = Optional(Json, default={}) + gate_chan = Optional("LogicalMarkerChannel") + receiver_chan = Optional("ReceiverChannel") class PhysicalMarkerChannel(PhysicalChannel): ''' A digital output channel on an AWG. + gate_buffer: How much extra time should be added onto the beginning of a gating pulse + gate_min_width: The minimum marker pulse width ''' - gate_buffer = Float(0.0).tag( - desc="How much extra time should be added onto the beginning of a gating pulse") - gate_min_width = Float(0.0).tag(desc="The minimum marker pulse width") - + gate_buffer = Required(float, default=0.0) + gate_min_width = Required(float, default=0.0) + phys_channel = Optional(PhysicalChannel) class PhysicalQuadratureChannel(PhysicalChannel): ''' Something used to implement a standard qubit channel with two analog channels and a microwave gating channel. ''' - I_channel = Str() - Q_channel = Str() - #During initilization we may just have a string reference to the channel - amp_factor = Float(1.0) - phase_skew = Float(0.0) - - @property - def correctionT(self): - return np.array( - [[self.amp_factor, self.amp_factor * tan(self.phase_skew * pi / 180)], - [0, 1 / cos(self.phase_skew * pi / 180)]]) + I_channel = Optional(PhysicalChannel) + Q_channel = Optional(PhysicalChannel) + amp_factor = Required(float, default=1.0) + phase_skew = Required(float, default=0.0) +# marker_channel = Optional(PhysicalMarkerChannel) class ReceiverChannel(PhysicalChannel): ''' A trigger input on a receiver. ''' - channel = Str() + triggering_channel = Optional(LogicalChannel) + channel = Optional(int) +def pulse_check(name): + return name in ["constant", "gaussian", "drag", "gaussOn", "gaussOff", "dragGaussOn", "dragGaussOff", + "tanh", "exp_decay", "autodyne", "arb_axis_drag"] + class LogicalMarkerChannel(LogicalChannel): ''' A class for digital channels for gating sources or triggering other things. ''' - pulse_params = Dict(default={'shape_fun': PulseShapes.constant, - 'length': 10e-9}) + meas_channel = Optional(LogicalChannel) + trig_channel = Optional("Measurement") + def __init__(self, label="lm", pulse_params=None): + if not pulse_params: + pulse_params = {'shape_fun': "constant",'length': 10e-9} + super(LogicalMarkerChannel, self).__init__(label=label, pulse_params=pulse_params) class Qubit(LogicalChannel): ''' The main class for generating qubit pulses. Effectively a logical "QuadratureChannel". - ''' - pulse_params = Dict(default={'length': 20e-9, - 'piAmp': 1.0, - 'pi2Amp': 0.5, - 'shape_fun': PulseShapes.gaussian, - 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9}) - gate_chan = Instance((str, LogicalMarkerChannel)) - frequency = Float(0.0).tag( - desc='modulation frequency of the channel (can be positive or negative)') - - def __init__(self, **kwargs): - super(Qubit, self).__init__(**kwargs) - + frequency: modulation frequency of the channel (can be positive or negative) + ''' + edge_source = Optional("Edge", reverse="source") + edge_target = Optional("Edge", reverse="target") + + def __init__(self, label="q", pulse_params=None): + if not pulse_params: + pulse_params = {'length': 20e-9, + 'piAmp': 1.0, + 'pi2Amp': 0.5, + 'shape_fun': "gaussian", + 'cutoff': 2, + 'drag_scaling': 0, + 'sigma': 5e-9} + super(Qubit, self).__init__(label=label, pulse_params=pulse_params) class Measurement(LogicalChannel): ''' A class for measurement channels. Measurements are special because they can be different types: autodyne which needs an IQ pair or hetero/homodyne which needs just a marker channel. + meas_type: Type of measurement (autodyne, homodyne) + autodyne_freq: use to bake the modulation into the pulse, so that it has constant phase + frequency: use to asssociate modulation with the channel ''' - meas_type = Enum('autodyne', 'homodyne').tag( - desc='Type of measurement (autodyne, homodyne)') + meas_type = Required(str, default='autodyne', py_check=lambda x: x in ['autodyne', 'homodyne']) + autodyne_freq = Required(float, default=0.0) + trig_chan = Optional(LogicalMarkerChannel) - autodyne_freq = Float(0.0).tag( - desc='use to bake the modulation into the pulse, so that it has constant phase') - frequency = Float(0.0).tag( - desc='use frequency to asssociate modulation with the channel') - pulse_params = Dict(default={'length': 100e-9, + def __init__(self, label="m", pulse_params=None): + if not pulse_params: + pulse_params = {'length': 100e-9, 'amp': 1.0, - 'shape_fun': PulseShapes.tanh, + 'shape_fun': "tanh", 'cutoff': 2, - 'sigma': 1e-9}) - gate_chan = Instance((str, LogicalMarkerChannel)) - trig_chan = Instance((str, LogicalMarkerChannel)) - receiver_chan = Instance((str, ReceiverChannel)) - - def __init__(self, **kwargs): - super(Measurement, self).__init__(**kwargs) - if self.trig_chan is None: - self.trig_chan = LogicalMarkerChannel(label='digitizerTrig') - + 'sigma': 1e-9} + super(Measurement, self).__init__(label=label, pulse_params=pulse_params) class Edge(LogicalChannel): ''' @@ -186,38 +162,18 @@ class Edge(LogicalChannel): Qubit channel. ''' # allow string in source and target so that we can store a label or an object - source = Instance((str, Qubit)) - target = Instance((str, Qubit)) - pulse_params = Dict(default={'length': 20e-9, - 'amp': 1.0, - 'phase': 0.0, - 'shape_fun': PulseShapes.gaussian, - 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9, - 'riseFall': 20e-9}) - gate_chan = Instance((str, LogicalMarkerChannel)) - frequency = Float(0.0).tag( - desc='modulation frequency of the channel (can be positive or negative)') - - def __init__(self, **kwargs): - super(Edge, self).__init__(**kwargs) - - def isforward(self, source, target): - ''' Test whether (source, target) matches the directionality of the edge. ''' - nodes = (self.source, self.target) - if (source not in nodes) or (target not in nodes): - raise ValueError('One of {0} is not a node in the edge'.format(( - source, target))) - if (self.source, self.target) == (source, target): - return True - else: - return False - - -NewLogicalChannelList = [Qubit, Edge, LogicalMarkerChannel, Measurement] -NewPhysicalChannelList = [ - PhysicalMarkerChannel, - PhysicalQuadratureChannel, - ReceiverChannel -] + source = Required(Qubit) + target = Required(Qubit) + + def __init__(self, label="e", pulse_params=None): + if not pulse_params: + pulse_params = {'length': 20e-9, + 'amp': 1.0, + 'phase': 0.0, + 'shape_fun': "gaussian", + 'cutoff': 2, + 'drag_scaling': 0, + 'sigma': 5e-9, + 'riseFall': 20e-9} + super(Edge, self).__init__(label=label, pulse_params=pulse_params) + diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 42cfd7fe..a8769f50 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -338,6 +338,8 @@ def compile_to_hardware(seqs, for seq in seqs: PatternUtils.add_gate_pulses(seq) + # ChannelLibraries.channelLib = {c.label: c for c in select(c for c in Channel)} + if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: # Add the slave trigger logger.debug("Adding slave trigger") diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 9f4d6913..7b79d9af 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -113,7 +113,10 @@ def shape(self): params = copy(self.shapeParams) params['sampling_rate'] = self.channel.phys_chan.sampling_rate params.pop('shape_fun') - return self.shapeParams['shape_fun'](**params) + if isinstance(self.shapeParams['shape_fun'],str): + return getattr(PulseShapes, self.shapeParams['shape_fun'])(**params) + else: + return self.shapeParams['shape_fun'](**params) def TAPulse(label, diff --git a/QGL/PulseShapes.py b/QGL/PulseShapes.py index 2ba601bf..17899d33 100644 --- a/QGL/PulseShapes.py +++ b/QGL/PulseShapes.py @@ -174,7 +174,10 @@ def autodyne(frequency=10e6, baseShape=constant, **params): ''' A pulse with modulation at a particular frequency baked in. ''' - shape = baseShape(**params) + if isinstance(baseShape,str): + shape = globals()[baseShape](**params) + else: + shape = baseShape(**params) # Apply the autodyne frequency timePts = np.linspace(0, params['length'], len(shape)) shape *= np.exp(-1j * 2 * np.pi * frequency * timePts) diff --git a/QGL/config.py b/QGL/config.py index ef088fd6..91b1a563 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -3,16 +3,20 @@ import os.path import re import yaml +from pony.orm import * + +# Here is a placeholder db +db = Database() # Where to store AWG data -AWGDir = None +AWGDir = None # The measurement file -meas_file = None +meas_file = None # plotting options -plotBackground = '#EAEAF2' -gridColor = None +plotBackground = '#EAEAF2' +gridColor = None # select pulse library (standard or all90) pulse_primitives_lib = "standard" From b9f3c0c3ff10830082119f5c9531ba3d6a6ab385 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 1 Jun 2018 10:23:27 -0400 Subject: [PATCH 038/235] Adding python configuration file, supporting infrastructure --- QGL/ChannelLibraries.py | 76 ++++++++-- QGL/Channels.py | 317 ++++++++++++++++++++++------------------ QGL/Compiler.py | 4 +- QGL/__init__.py | 2 +- QGL/config.py | 86 +---------- QGL/config_location.py | 2 +- 6 files changed, 243 insertions(+), 244 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 1078716d..379bb375 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -3,8 +3,10 @@ real channels. Split from Channels.py on Jan 14, 2016. +Moved to pony ORM from atom June 1, 2018 Original Author: Colm Ryan +Modified By: Graham Rowlands Copyright 2016 Raytheon BBN Technologies @@ -33,18 +35,9 @@ import traceback import datetime import importlib +import inspect from pony.orm import * import networkx as nx -import yaml - -# FSEvents observer in watchdog cannot have multiple watchers of the same path -# use kqueue instead -if sys.platform == 'darwin': - from watchdog.observers.kqueue import KqueueObserver as Observer -else: - from watchdog.observers import Observer -from watchdog.events import FileSystemEventHandler -import time from . import config from . import Channels @@ -62,21 +55,34 @@ def set_from_dict(obj, settings): class ChannelLibrary(object): - def __init__(self, library_file=None, blank=False, channelDict={}, **kwargs): - """Create the channel library. We assume that the user wants the config file in the - usual locations specified in the config files.""" + def __init__(self, database_file=":memory:", blank=False, channelDict={}, **kwargs): + """Create the channel library.""" + + db = Database() + Channels.define_entities(db) + db.bind('sqlite', filename=database_file) + db.generate_mapping(create_tables=True) - # Load the basic config options from the yaml - self.library_file = config.load_config(library_file) + config.load_config() + + # Dirty trick: push the correct entity defs to the calling context + for var in ["Measurement","Qubit","Edge"]: + inspect.stack()[1][0].f_globals[var] = getattr(Channels, var) + # print(a) + # import ipdb; ipdb.set_trace() self.connectivityG = nx.DiGraph() - self.channelDict = {c.label: c for c in select(c for c in Channels.Channel)} + # This is still somewhere legacy QGL behavior. Massage db into dict for lookup. + self.channelDict = {} # Update the global reference global channelLib channelLib = self + def update_channelDict(self): + self.channelDict = {c.label: c for c in select(c for c in Channels.Channel)} + #Dictionary methods def __getitem__(self, key): return self.channelDict[key] @@ -302,6 +308,44 @@ def load_from_library(self, return_only=False): self.connectivityG[chan.source][chan.target]['channel'] = chan >>>>>>> Ditch atom, move to Pony.orm for all channel library objects. +# Convenience functions for generating and linking channels +class APS2(object): + def __init__(self, label): + self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern") + self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern") + self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern") + self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern") + self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern") + +class X6(object): + def __init__(self, label): + self.chan1 = Channels.ReceiverChannel(label=f"{label}-1") + self.chan2 = Channels.ReceiverChannel(label=f"{label}-2") + available_streams = ["raw", "demodulated", "integrated", "averaged"] + +def new_qubit(label): + return Channels.Qubit(label=label) + +def set_control(qubit, awg): + qubit.phys_chan = awg.chan12 + +def set_measure(qubit, awg, dig, dig_channel=1, trig_channel=1, gate=False, gate_channel=2, trigger_length=1e-7): + meas = Channels.Measurement(label=f"M-{qubit.label}") + meas.phys_chan = awg.chan12 + + meas.trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}") + meas.trig_chan.phys_chan = getattr(awg, f"m{trig_channel}") + meas.trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} + + if gate: + meas.gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate") + meas.gate_chan.phys_chan = getattr(awg, f"m{gate_channel}") + +def set_master(awg, trig_channel=2, pulse_length=1e-7): + st = Channels.LogicalMarkerChannel(label="slave_trig") + st.phys_chan = getattr(awg, f"m{trig_channel}") + st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} + def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' diff --git a/QGL/Channels.py b/QGL/Channels.py index 3a9b7b8b..4bcbf219 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -28,152 +28,177 @@ from pony.orm import * from copy import deepcopy - -class Channel(config.db.Entity): - ''' - Every channel has a label and some printers. - ''' - label = Required(str) - - def __repr__(self): - return str(self) - def __str__(self): - return "{0}('{1}')".format(self.__class__.__name__, self.label) - -class PhysicalChannel(Channel): - ''' - The main class for actual AWG channels. - ''' - instrument = Optional(str) # i.e. the AWG or receiver - translator = Optional(str) - generator = Optional(str) - sampling_rate = Optional(float, default=1.2e9) - delay = Required(float, default=0.0) - - # Required reverse connections - logical_channel = Optional("LogicalChannel") - quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") - quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") - marker_channel = Optional("PhysicalMarkerChannel") - -class LogicalChannel(Channel): - ''' - The main class from which we will generate sequences. - At some point it needs to be assigned to a physical channel. - frequency: modulation frequency of the channel (can be positive or negative) - ''' - #During initilization we may just have a string reference to the channel - phys_chan = Optional(PhysicalChannel) - frequency = Required(float, default=0.0) - pulse_params = Optional(Json, default={}) - gate_chan = Optional("LogicalMarkerChannel") - receiver_chan = Optional("ReceiverChannel") - -class PhysicalMarkerChannel(PhysicalChannel): - ''' - A digital output channel on an AWG. - gate_buffer: How much extra time should be added onto the beginning of a gating pulse - gate_min_width: The minimum marker pulse width - ''' - gate_buffer = Required(float, default=0.0) - gate_min_width = Required(float, default=0.0) - phys_channel = Optional(PhysicalChannel) - -class PhysicalQuadratureChannel(PhysicalChannel): - ''' - Something used to implement a standard qubit channel with two analog channels and a microwave gating channel. - ''' - I_channel = Optional(PhysicalChannel) - Q_channel = Optional(PhysicalChannel) - amp_factor = Required(float, default=1.0) - phase_skew = Required(float, default=0.0) -# marker_channel = Optional(PhysicalMarkerChannel) - -class ReceiverChannel(PhysicalChannel): - ''' - A trigger input on a receiver. - ''' - triggering_channel = Optional(LogicalChannel) - channel = Optional(int) - -def pulse_check(name): - return name in ["constant", "gaussian", "drag", "gaussOn", "gaussOff", "dragGaussOn", "dragGaussOff", - "tanh", "exp_decay", "autodyne", "arb_axis_drag"] - -class LogicalMarkerChannel(LogicalChannel): - ''' - A class for digital channels for gating sources or triggering other things. - ''' - meas_channel = Optional(LogicalChannel) - trig_channel = Optional("Measurement") - - def __init__(self, label="lm", pulse_params=None): - if not pulse_params: - pulse_params = {'shape_fun': "constant",'length': 10e-9} - super(LogicalMarkerChannel, self).__init__(label=label, pulse_params=pulse_params) - -class Qubit(LogicalChannel): - ''' - The main class for generating qubit pulses. Effectively a logical "QuadratureChannel". - frequency: modulation frequency of the channel (can be positive or negative) - ''' - edge_source = Optional("Edge", reverse="source") - edge_target = Optional("Edge", reverse="target") - - def __init__(self, label="q", pulse_params=None): - if not pulse_params: - pulse_params = {'length': 20e-9, - 'piAmp': 1.0, - 'pi2Amp': 0.5, - 'shape_fun': "gaussian", - 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9} - super(Qubit, self).__init__(label=label, pulse_params=pulse_params) - -class Measurement(LogicalChannel): - ''' - A class for measurement channels. - Measurements are special because they can be different types: - autodyne which needs an IQ pair or hetero/homodyne which needs just a marker channel. - meas_type: Type of measurement (autodyne, homodyne) - autodyne_freq: use to bake the modulation into the pulse, so that it has constant phase - frequency: use to asssociate modulation with the channel - ''' - meas_type = Required(str, default='autodyne', py_check=lambda x: x in ['autodyne', 'homodyne']) - autodyne_freq = Required(float, default=0.0) - trig_chan = Optional(LogicalMarkerChannel) - - def __init__(self, label="m", pulse_params=None): - if not pulse_params: - pulse_params = {'length': 100e-9, - 'amp': 1.0, - 'shape_fun': "tanh", +# Get these in global scope for module imports +Channel = None +PhysicalChannel = None +LogicalChannel = None +PhysicalQuadratureChannel = None +PhysicalMarkerChannel = None +LogicalMarkerChannel = None +ReceiverChannel = None +Measurement = None +Qubit = None +Edge = None + +def define_entities(db): + + class Channel(db.Entity): + ''' + Every channel has a label and some printers. + ''' + label = Required(str) + + def __repr__(self): + return str(self) + def __str__(self): + return "{0}('{1}')".format(self.__class__.__name__, self.label) + + class PhysicalChannel(Channel): + ''' + The main class for actual AWG channels. + ''' + instrument = Optional(str) # i.e. the AWG or receiver + translator = Optional(str) + generator = Optional(str) + sampling_rate = Optional(float, default=1.2e9) + delay = Required(float, default=0.0) + + # Required reverse connections + logical_channel = Optional("LogicalChannel") + quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") + quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") + marker_channel = Optional("PhysicalMarkerChannel") + + class LogicalChannel(Channel): + ''' + The main class from which we will generate sequences. + At some point it needs to be assigned to a physical channel. + frequency: modulation frequency of the channel (can be positive or negative) + ''' + #During initilization we may just have a string reference to the channel + phys_chan = Optional(PhysicalChannel) + frequency = Required(float, default=0.0) + pulse_params = Optional(Json, default={}) + gate_chan = Optional("LogicalMarkerChannel") + receiver_chan = Optional("ReceiverChannel") + + class PhysicalMarkerChannel(PhysicalChannel): + ''' + A digital output channel on an AWG. + gate_buffer: How much extra time should be added onto the beginning of a gating pulse + gate_min_width: The minimum marker pulse width + ''' + gate_buffer = Required(float, default=0.0) + gate_min_width = Required(float, default=0.0) + phys_channel = Optional(PhysicalChannel) + + class PhysicalQuadratureChannel(PhysicalChannel): + ''' + Something used to implement a standard qubit channel with two analog channels and a microwave gating channel. + ''' + I_channel = Optional(PhysicalChannel) + Q_channel = Optional(PhysicalChannel) + amp_factor = Required(float, default=1.0) + phase_skew = Required(float, default=0.0) + # marker_channel = Optional(PhysicalMarkerChannel) + + class ReceiverChannel(PhysicalChannel): + ''' + A trigger input on a receiver. + ''' + triggering_channel = Optional(LogicalChannel) + channel = Optional(int) + + def pulse_check(name): + return name in ["constant", "gaussian", "drag", "gaussOn", "gaussOff", "dragGaussOn", "dragGaussOff", + "tanh", "exp_decay", "autodyne", "arb_axis_drag"] + + class LogicalMarkerChannel(LogicalChannel): + ''' + A class for digital channels for gating sources or triggering other things. + ''' + meas_channel = Optional(LogicalChannel) + trig_channel = Optional("Measurement") + + def __init__(self, label="lm", pulse_params=None): + if not pulse_params: + pulse_params = {'shape_fun': "constant",'length': 10e-9} + super(LogicalMarkerChannel, self).__init__(label=label, pulse_params=pulse_params) + + class Qubit(LogicalChannel): + ''' + The main class for generating qubit pulses. Effectively a logical "QuadratureChannel". + frequency: modulation frequency of the channel (can be positive or negative) + ''' + edge_source = Optional("Edge", reverse="source") + edge_target = Optional("Edge", reverse="target") + + def __init__(self, label="q", pulse_params=None): + if not pulse_params: + pulse_params = {'length': 20e-9, + 'piAmp': 1.0, + 'pi2Amp': 0.5, + 'shape_fun': "gaussian", 'cutoff': 2, - 'sigma': 1e-9} - super(Measurement, self).__init__(label=label, pulse_params=pulse_params) - -class Edge(LogicalChannel): - ''' - Defines an arc/directed edge between qubit vertices. If a device supports bi-directional - connectivity, that is represented with two independent Edges. - - An Edge is also effectively an abstract channel, so it carries the same properties as a - Qubit channel. - ''' - # allow string in source and target so that we can store a label or an object - source = Required(Qubit) - target = Required(Qubit) - - def __init__(self, label="e", pulse_params=None): - if not pulse_params: - pulse_params = {'length': 20e-9, + 'drag_scaling': 0, + 'sigma': 5e-9} + super(Qubit, self).__init__(label=label, pulse_params=pulse_params) + + class Measurement(LogicalChannel): + ''' + A class for measurement channels. + Measurements are special because they can be different types: + autodyne which needs an IQ pair or hetero/homodyne which needs just a marker channel. + meas_type: Type of measurement (autodyne, homodyne) + autodyne_freq: use to bake the modulation into the pulse, so that it has constant phase + frequency: use to asssociate modulation with the channel + ''' + meas_type = Required(str, default='autodyne', py_check=lambda x: x in ['autodyne', 'homodyne']) + autodyne_freq = Required(float, default=0.0) + trig_chan = Optional(LogicalMarkerChannel) + + def __init__(self, label="m", pulse_params=None): + if not pulse_params: + pulse_params = {'length': 100e-9, 'amp': 1.0, - 'phase': 0.0, - 'shape_fun': "gaussian", + 'shape_fun': "tanh", 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9, - 'riseFall': 20e-9} - super(Edge, self).__init__(label=label, pulse_params=pulse_params) - + 'sigma': 1e-9} + super(Measurement, self).__init__(label=label, pulse_params=pulse_params) + + class Edge(LogicalChannel): + ''' + Defines an arc/directed edge between qubit vertices. If a device supports bi-directional + connectivity, that is represented with two independent Edges. + + An Edge is also effectively an abstract channel, so it carries the same properties as a + Qubit channel. + ''' + # allow string in source and target so that we can store a label or an object + source = Required(Qubit) + target = Required(Qubit) + + def __init__(self, label="e", pulse_params=None): + if not pulse_params: + pulse_params = {'length': 20e-9, + 'amp': 1.0, + 'phase': 0.0, + 'shape_fun': "gaussian", + 'cutoff': 2, + 'drag_scaling': 0, + 'sigma': 5e-9, + 'riseFall': 20e-9} + super(Edge, self).__init__(label=label, pulse_params=pulse_params) + + globals()["Channel"] = Channel + globals()["PhysicalChannel"] = PhysicalChannel + globals()["LogicalChannel"] = LogicalChannel + globals()["PhysicalQuadratureChannel"] = PhysicalQuadratureChannel + globals()["PhysicalMarkerChannel"] = PhysicalMarkerChannel + globals()["LogicalMarkerChannel"] = LogicalMarkerChannel + globals()["ReceiverChannel"] = ReceiverChannel + globals()["Measurement"] = Measurement + globals()["Qubit"] = Qubit + globals()["Edge"] = Edge + + \ No newline at end of file diff --git a/QGL/Compiler.py b/QGL/Compiler.py index a8769f50..717e9a4d 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -318,6 +318,8 @@ def compile_to_hardware(seqs, add_slave_trigger (optional): add the slave trigger(s) tdm_seq (optional): compile for TDM ''' + ChannelLibraries.channelLib.update_channelDict() + logger.debug("Compiling %d sequence(s)", len(seqs)) # save input code to file @@ -338,8 +340,6 @@ def compile_to_hardware(seqs, for seq in seqs: PatternUtils.add_gate_pulses(seq) - # ChannelLibraries.channelLib = {c.label: c for c in select(c for c in Channel)} - if add_slave_trigger and 'slave_trig' in ChannelLibraries.channelLib: # Add the slave trigger logger.debug("Adding slave trigger") diff --git a/QGL/__init__.py b/QGL/__init__.py index 2433b99a..6f8a6e68 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -1,5 +1,5 @@ from .Channels import Qubit, Measurement, Edge -from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib +from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib, APS2, X6, new_qubit, set_control, set_measure, set_master from .PulsePrimitives import * from .Compiler import compile_to_hardware, set_log_level from .PulseSequencer import align diff --git a/QGL/config.py b/QGL/config.py index 91b1a563..c54e5666 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -1,12 +1,9 @@ #Package configuration information import os.path +import sys import re -import yaml -from pony.orm import * - -# Here is a placeholder db -db = Database() +import importlib # Where to store AWG data AWGDir = None @@ -25,77 +22,10 @@ # CNOT in your gate set, e.g. CNOT_simple or CNOT_CR) cnot_implementation = "CNOT_simple" -class LoaderMeta(type): - def __new__(metacls, __name__, __bases__, __dict__): - """Add include constructer to class.""" - # register the include constructor on the class - cls = super().__new__(metacls, __name__, __bases__, __dict__) - cls.add_constructor('!include', cls.construct_include) - return cls -class Loader(yaml.Loader, metaclass=LoaderMeta): - """YAML Loader with `!include` constructor.""" - def __init__(self, stream): - """Initialise Loader.""" - try: - self._root = os.path.split(stream.name)[0] - except AttributeError: - self._root = os.path.curdir - super().__init__(stream) - self.add_implicit_resolver( - u'tag:yaml.org,2002:float', - re.compile(u'''^(?: - [-+]?(?:[0-9][0-9_]*)\\.[0-9_]*(?:[eE][-+]?[0-9]+)? - |[-+]?(?:[0-9][0-9_]*)(?:[eE][-+]?[0-9]+) - |\\.[0-9_]+(?:[eE][-+][0-9]+)? - |[-+]?[0-9][0-9_]*(?::[0-5]?[0-9])+\\.[0-9_]* - |[-+]?\\.(?:inf|Inf|INF) - |\\.(?:nan|NaN|NAN))$''', re.X), - list(u'-+0123456789.')) - self.filenames = [os.path.abspath(stream.name)] - def construct_include(self, node): - """Include file referenced at node.""" - filename = os.path.abspath(os.path.join( - self._root, self.construct_scalar(node) - )) - extension = os.path.splitext(filename)[1].lstrip('.') - self.filenames.append(filename) - with open(filename, 'r') as f: - if extension in ('yaml', 'yml'): - return yaml.load(f, Loader) - else: - return ''.join(f.readlines()) - -def find_meas_file(): - if os.getenv('BBN_MEAS_FILE'): - return os.getenv('BBN_MEAS_FILE') - raise Exception("Could not find the measurement file in the environment variables or the auspex globals.") - -def load_config(filename=None): - global meas_file, AWGDir, plotBackground, gridColor, pulse_primitives_lib, cnot_implementation - - if filename: - meas_file = filename +def load_config(): + if os.getenv('BBN_CONFIG_FILE'): + cfg = os.getenv("BBN_CONFIG_FILE") + sys.path.append(os.path.dirname(cfg)) + importlib.import_module(os.path.splitext(os.path.basename(cfg))[0]) else: - meas_file = find_meas_file() - - with open(meas_file, 'r') as FID: - # cfg = yaml.load(f) - loader = Loader(FID) - try: - cfg = loader.get_single_data() - finally: - loader.dispose() - - # pull out the variables - # abspath allows the use of relative file names in the config file - if 'AWGDir' in cfg['config'].keys(): - AWGDir = os.path.abspath(cfg['config']['AWGDir']) - else: - raise KeyError("Could not find AWGDir in the YAML config section") - - plotBackground = cfg['config'].get('PlotBackground', '#EAEAF2') - gridColor = cfg['config'].get('GridColor', None) - pulse_primitives_lib = cfg['config'].get('PulsePrimitivesLibrary', 'standard') - cnot_implementation = cfg['config'].get('cnot_implementation', 'CNOT_simple') - - return meas_file \ No newline at end of file + raise Exception("Could not find the measurement file in the environment variables or the auspex globals.") diff --git a/QGL/config_location.py b/QGL/config_location.py index 16566195..d9304dec 100644 --- a/QGL/config_location.py +++ b/QGL/config_location.py @@ -50,7 +50,7 @@ CONFIG_PATH = None -_CONFIG_FILE_NAME = 'config.json' +_CONFIG_FILE_NAME = 'BBN_config.py' def _set_default_config_path(): """ From 2e63ef90ca47fc4bc101ff9ac2e18dc30f816bf7 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 4 Jun 2018 21:45:53 -0400 Subject: [PATCH 039/235] Can now load/save to a database with labels and datestamps --- QGL/ChannelLibraries.py | 109 +++++++++++++++++++++++++++++++++------- QGL/Channels.py | 27 ++++++++-- QGL/Compiler.py | 2 +- QGL/__init__.py | 4 +- QGL/config.py | 12 +++-- 5 files changed, 125 insertions(+), 29 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 379bb375..c0eac234 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -32,6 +32,7 @@ import sys import os import re +import datetime import traceback import datetime import importlib @@ -53,35 +54,86 @@ def set_from_dict(obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") +def copy_entity(obj): + """Copy a pony entity instance""" + kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_} + kwargs.pop("id") + kwargs.pop("classtype") + return obj.__class__(**kwargs) + class ChannelLibrary(object): - def __init__(self, database_file=":memory:", blank=False, channelDict={}, **kwargs): + def __init__(self, channel_db_name=None, database_file=":memory:", channelDict={}, **kwargs): """Create the channel library.""" db = Database() Channels.define_entities(db) - db.bind('sqlite', filename=database_file) + db.bind('sqlite', filename=database_file, create_db=True) db.generate_mapping(create_tables=True) - config.load_config() - # Dirty trick: push the correct entity defs to the calling context for var in ["Measurement","Qubit","Edge"]: inspect.stack()[1][0].f_globals[var] = getattr(Channels, var) - # print(a) - # import ipdb; ipdb.set_trace() self.connectivityG = nx.DiGraph() # This is still somewhere legacy QGL behavior. Massage db into dict for lookup. self.channelDict = {} + self.channels = [] + self.sources = [] + self.channelDatabase = None + self.channel_db_name = channel_db_name if channel_db_name else "temp" + + self.load_most_recent() + # config.load_config() # Update the global reference global channelLib channelLib = self - def update_channelDict(self): - self.channelDict = {c.label: c for c in select(c for c in Channels.Channel)} + def list(self): + select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).show() + + def load_by_id(self, id_num): + obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() + self.load(obj) + + self.channels = list(obj.channels) + self.sources = list(obj.sources) + self.channel_db_name = obj.label + + def load(self, obj): + self.channels = list(obj.channels) + self.sources = list(obj.sources) + self.channel_db_name = obj.label + self.channelDatabase = obj + + def load_most_recent(self, name=None): + if name is None: + name = self.channel_db_name + mrcd = Channels.ChannelDatabase.select(lambda d: d.label==name).order_by(desc(Channels.ChannelDatabase.time)).first() + if mrcd: + self.load(mrcd) + + def new(self, name): + self.channelDatabase = None + self.channel_db_name = name + self.channels = [] + self.sources = [] + + def save(self): + self.save_as(self.channel_db_name) + + def save_as(self, name): + # Get channels that are part of the currently active db, or find those that aren't yet part of a db + chans = list(select(c for c in Channels.Channel if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) + srcs = list(select(c for c in Channels.MicrowaveSource if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) + cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now(), channels=chans, sources=srcs) + self.channels = chans + self.sources = srcs + self.channelDatabase = cd + commit() + self.channel_db_name = name #Dictionary methods def __getitem__(self, key): @@ -309,34 +361,56 @@ def load_from_library(self, return_only=False): >>>>>>> Ditch atom, move to Pony.orm for all channel library objects. # Convenience functions for generating and linking channels +# TODO: move these to a shim layer shared by Auspex/QGL + class APS2(object): - def __init__(self, label): + def __init__(self, label, address=None, delay=0.0): self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern") self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern") self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern") self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern") self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern") + self.trigger_interval = None + self.trigger_source = "External" + self.address = address + self.delay = delay + self.master = False + class X6(object): - def __init__(self, label): - self.chan1 = Channels.ReceiverChannel(label=f"{label}-1") - self.chan2 = Channels.ReceiverChannel(label=f"{label}-2") - available_streams = ["raw", "demodulated", "integrated", "averaged"] + def __init__(self, label, address=None): + self.chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1") + self.chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2") + + self.address = address + self.reference = "external" + self.nbr_segments = 1 + self.nbr_round_robins = 100 + self.acquire_mode = "digitizer" def new_qubit(label): return Channels.Qubit(label=label) -def set_control(qubit, awg): +def new_source(label, source_type, address, power=-30.0): + return Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power) + +def set_control(qubit, awg, generator=None): qubit.phys_chan = awg.chan12 + if generator: + qubit.phys_chan.generator = generator -def set_measure(qubit, awg, dig, dig_channel=1, trig_channel=1, gate=False, gate_channel=2, trigger_length=1e-7): +def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=1, gate=False, gate_channel=2, trigger_length=1e-7): meas = Channels.Measurement(label=f"M-{qubit.label}") meas.phys_chan = awg.chan12 meas.trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}") meas.trig_chan.phys_chan = getattr(awg, f"m{trig_channel}") meas.trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} - + meas.receiver_chan = getattr(dig, f"chan{dig_channel}") + + if generator: + meas.phys_chan.generator = generator + if gate: meas.gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate") meas.gate_chan.phys_chan = getattr(awg, f"m{gate_channel}") @@ -345,7 +419,8 @@ def set_master(awg, trig_channel=2, pulse_length=1e-7): st = Channels.LogicalMarkerChannel(label="slave_trig") st.phys_chan = getattr(awg, f"m{trig_channel}") st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} - + awg.master = True + awg.trigger_source = "Internal" def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' diff --git a/QGL/Channels.py b/QGL/Channels.py index 4bcbf219..2b8ea34b 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -27,6 +27,7 @@ from math import tan, cos, pi from pony.orm import * from copy import deepcopy +import datetime # Get these in global scope for module imports Channel = None @@ -39,14 +40,31 @@ Measurement = None Qubit = None Edge = None +MicrowaveSource = None +ChannelDatabase = None def define_entities(db): + class ChannelDatabase(db.Entity): + label = Required(str) + channels = Set("Channel") + sources = Set("MicrowaveSource") + time = Optional(datetime.datetime) + + class MicrowaveSource(db.Entity): + label = Required(str) + source_type = Required(str) + address = Optional(str) + power = Optional(float) + logical_channel = Optional("PhysicalChannel") + channel_db = Optional("ChannelDatabase") + class Channel(db.Entity): ''' Every channel has a label and some printers. ''' - label = Required(str) + label = Required(str) + channel_db = Optional("ChannelDatabase") def __repr__(self): return str(self) @@ -59,8 +77,8 @@ class PhysicalChannel(Channel): ''' instrument = Optional(str) # i.e. the AWG or receiver translator = Optional(str) - generator = Optional(str) - sampling_rate = Optional(float, default=1.2e9) + generator = Optional(MicrowaveSource, reverse="logical_channel") + sampling_rate = Required(float, default=1.2e9) delay = Required(float, default=0.0) # Required reverse connections @@ -200,5 +218,6 @@ def __init__(self, label="e", pulse_params=None): globals()["Measurement"] = Measurement globals()["Qubit"] = Qubit globals()["Edge"] = Edge - + globals()["MicrowaveSource"] = MicrowaveSource + globals()["ChannelDatabase"] = ChannelDatabase \ No newline at end of file diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 717e9a4d..8ceaa8c4 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -318,7 +318,7 @@ def compile_to_hardware(seqs, add_slave_trigger (optional): add the slave trigger(s) tdm_seq (optional): compile for TDM ''' - ChannelLibraries.channelLib.update_channelDict() + # ChannelLibraries.channelLib.update_channelDict() logger.debug("Compiling %d sequence(s)", len(seqs)) diff --git a/QGL/__init__.py b/QGL/__init__.py index 6f8a6e68..e11ea869 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -1,5 +1,5 @@ -from .Channels import Qubit, Measurement, Edge -from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib, APS2, X6, new_qubit, set_control, set_measure, set_master +from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase +from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib, APS2, X6, new_qubit, set_control, set_measure, set_master, new_source from .PulsePrimitives import * from .Compiler import compile_to_hardware, set_log_level from .PulseSequencer import align diff --git a/QGL/config.py b/QGL/config.py index c54e5666..152cb750 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -24,8 +24,10 @@ def load_config(): if os.getenv('BBN_CONFIG_FILE'): - cfg = os.getenv("BBN_CONFIG_FILE") - sys.path.append(os.path.dirname(cfg)) - importlib.import_module(os.path.splitext(os.path.basename(cfg))[0]) - else: - raise Exception("Could not find the measurement file in the environment variables or the auspex globals.") + try: + cfg = os.getenv("BBN_CONFIG_FILE") + sys.path.append(os.path.dirname(cfg)) + importlib.import_module(os.path.splitext(os.path.basename(cfg))[0]) + except: + raise Exception("Could not find the measurement file in the environment variables or the auspex globals.") + \ No newline at end of file From 4c83141cdcbefab1b916030f3ab7a537562e04f5 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 6 Jun 2018 10:51:50 -0400 Subject: [PATCH 040/235] Fix factories to pull most recent qubits, etc. --- QGL/ChannelLibraries.py | 69 +++++++++++++++++++++++++++++++++++++---- QGL/Channels.py | 46 +++++++++++++-------------- QGL/Compiler.py | 2 +- QGL/config.py | 22 ++++++++----- 4 files changed, 102 insertions(+), 37 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index c0eac234..fc4294fb 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -54,21 +54,65 @@ def set_from_dict(obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") +def copy_objs(chans, srcs): + new_chans = [] + new_srcs = [] + old_to_new_chan = {} + old_to_new_src = {} + + for chan in chans: + c = copy_entity(chan) + print("Copied", chan, "to", c) + new_chans.append(c) + old_to_new_chan[chan] = c + + for src in srcs: + c = copy_entity(src) + print("Copied", src, "to", c) + new_srcs.append(c) + old_to_new_src[src] = c + + # # Fix links... pony updates the relationships symmetriacally so we get some for free + # for thing in new_chans + new_srcs: + # print(f"Fixing {thing}") + # for attr in thing._attrs_: + # print(f"\t{attr}") + # if attr: + # if isinstance(getattr(thing, attr.name), Channels.Channel): + # if getattr(thing, attr.name) in old_to_new_chan.keys(): + # print(f"setting {thing} {attr.name} to {old_to_new_chan[getattr(thing, attr.name)]}") + # setattr(thing, attr.name, old_to_new_chan[getattr(thing, attr.name)]) + # elif isinstance(getattr(thing, attr.name), Channels.MicrowaveSource): + # if getattr(thing, attr.name) in old_to_new_src.keys(): + # print(f"setting {thing} {attr.name} to {old_to_new_src[getattr(thing, attr.name)]}") + # setattr(thing, attr.name, old_to_new_src[getattr(thing, attr.name)]) + + return new_chans, new_srcs + def copy_entity(obj): """Copy a pony entity instance""" kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_} kwargs.pop("id") - kwargs.pop("classtype") + # kwargs.pop("classtype") + kwargs.pop("channel_db") return obj.__class__(**kwargs) class ChannelLibrary(object): - def __init__(self, channel_db_name=None, database_file=":memory:", channelDict={}, **kwargs): + def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **kwargs): """Create the channel library.""" + config.load_db() + if database_file: + self.database_file = database_file + elif config.db_file: + self.database_file = config.db_file + else: + self.database_file = ":memory:" + db = Database() Channels.define_entities(db) - db.bind('sqlite', filename=database_file, create_db=True) + db.bind('sqlite', filename=self.database_file, create_db=True) db.generate_mapping(create_tables=True) # Dirty trick: push the correct entity defs to the calling context @@ -84,6 +128,8 @@ def __init__(self, channel_db_name=None, database_file=":memory:", channelDict={ self.channelDatabase = None self.channel_db_name = channel_db_name if channel_db_name else "temp" + config.load_config() + self.load_most_recent() # config.load_config() @@ -91,6 +137,12 @@ def __init__(self, channel_db_name=None, database_file=":memory:", channelDict={ global channelLib channelLib = self + def get_current_channels(self): + return list(select(c for c in Channels.Channel if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) + + def update_channelDict(self): + self.channelDict = {c.label: c for c in self.get_current_channels()} + def list(self): select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).show() @@ -128,6 +180,9 @@ def save_as(self, name): # Get channels that are part of the currently active db, or find those that aren't yet part of a db chans = list(select(c for c in Channels.Channel if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) srcs = list(select(c for c in Channels.MicrowaveSource if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) + + # chans, src = copy_objs(chans, srcs) + cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now(), channels=chans, sources=srcs) self.channels = chans self.sources = srcs @@ -424,7 +479,9 @@ def set_master(awg, trig_channel=2, pulse_length=1e-7): def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' - thing = select(el for el in Channels.Qubit if el.label==label).first() + # TODO: this will just get the first entry in the whole damned DB! + # thing = select(el for el in Channels.Qubit if el.label==label).first() + thing = {c.label: c for c in channelLib.get_current_channels()}[label] if thing: return thing else: @@ -432,7 +489,7 @@ def QubitFactory(label, **kwargs): def MeasFactory(label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' - thing = select(el for el in Channels.Measurement if el.label==label).first() + thing = {c.label: c for c in channelLib.get_current_channels()}[label] if thing: return thing else: @@ -440,7 +497,7 @@ def MeasFactory(label, **kwargs): def MarkerFactory(label, **kwargs): ''' Return a saved Marker channel or create a new one. ''' - thing = select(el for el in Channels.LogicalMarkerChannel if el.label==label).first() + thing = {c.label: c for c in channelLib.get_current_channels()}[label] if thing: return thing else: diff --git a/QGL/Channels.py b/QGL/Channels.py index 2b8ea34b..274a0d47 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -138,10 +138,10 @@ class LogicalMarkerChannel(LogicalChannel): meas_channel = Optional(LogicalChannel) trig_channel = Optional("Measurement") - def __init__(self, label="lm", pulse_params=None): - if not pulse_params: - pulse_params = {'shape_fun': "constant",'length': 10e-9} - super(LogicalMarkerChannel, self).__init__(label=label, pulse_params=pulse_params) + def __init__(self, **kwargs): + if "pulse_params" not in kwargs.keys(): + kwargs["pulse_params"] = {'shape_fun': "constant",'length': 10e-9} + super(LogicalMarkerChannel, self).__init__(**kwargs) class Qubit(LogicalChannel): ''' @@ -151,16 +151,16 @@ class Qubit(LogicalChannel): edge_source = Optional("Edge", reverse="source") edge_target = Optional("Edge", reverse="target") - def __init__(self, label="q", pulse_params=None): - if not pulse_params: - pulse_params = {'length': 20e-9, + def __init__(self, **kwargs): + if "pulse_params" not in kwargs.keys(): + kwargs["pulse_params"] = {'length': 20e-9, 'piAmp': 1.0, 'pi2Amp': 0.5, 'shape_fun': "gaussian", 'cutoff': 2, 'drag_scaling': 0, 'sigma': 5e-9} - super(Qubit, self).__init__(label=label, pulse_params=pulse_params) + super(Qubit, self).__init__(**kwargs) class Measurement(LogicalChannel): ''' @@ -175,14 +175,14 @@ class Measurement(LogicalChannel): autodyne_freq = Required(float, default=0.0) trig_chan = Optional(LogicalMarkerChannel) - def __init__(self, label="m", pulse_params=None): - if not pulse_params: - pulse_params = {'length': 100e-9, + def __init__(self, **kwargs): + if "pulse_params" not in kwargs.keys(): + kwargs["pulse_params"] = {'length': 100e-9, 'amp': 1.0, 'shape_fun': "tanh", 'cutoff': 2, 'sigma': 1e-9} - super(Measurement, self).__init__(label=label, pulse_params=pulse_params) + super(Measurement, self).__init__(**kwargs) class Edge(LogicalChannel): ''' @@ -196,17 +196,17 @@ class Edge(LogicalChannel): source = Required(Qubit) target = Required(Qubit) - def __init__(self, label="e", pulse_params=None): - if not pulse_params: - pulse_params = {'length': 20e-9, - 'amp': 1.0, - 'phase': 0.0, - 'shape_fun': "gaussian", - 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9, - 'riseFall': 20e-9} - super(Edge, self).__init__(label=label, pulse_params=pulse_params) + def __init__(self, **kwargs): + if "pulse_params" not in kwargs.keys(): + kwargs["pulse_params"] = {'length': 20e-9, + 'amp': 1.0, + 'phase': 0.0, + 'shape_fun': "gaussian", + 'cutoff': 2, + 'drag_scaling': 0, + 'sigma': 5e-9, + 'riseFall': 20e-9} + super(Edge, self).__init__(**kwargs) globals()["Channel"] = Channel globals()["PhysicalChannel"] = PhysicalChannel diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 8ceaa8c4..717e9a4d 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -318,7 +318,7 @@ def compile_to_hardware(seqs, add_slave_trigger (optional): add the slave trigger(s) tdm_seq (optional): compile for TDM ''' - # ChannelLibraries.channelLib.update_channelDict() + ChannelLibraries.channelLib.update_channelDict() logger.debug("Compiling %d sequence(s)", len(seqs)) diff --git a/QGL/config.py b/QGL/config.py index 152cb750..085c48d8 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -8,8 +8,11 @@ # Where to store AWG data AWGDir = None -# The measurement file -meas_file = None +# The db file, where the channel libraries are stored +db_file = None + +# The config file (executed upon channel library loading) +config_file = None # plotting options plotBackground = '#EAEAF2' @@ -23,11 +26,16 @@ cnot_implementation = "CNOT_simple" def load_config(): + global config_file if os.getenv('BBN_CONFIG_FILE'): try: - cfg = os.getenv("BBN_CONFIG_FILE") - sys.path.append(os.path.dirname(cfg)) - importlib.import_module(os.path.splitext(os.path.basename(cfg))[0]) + config_file = os.getenv("BBN_CONFIG_FILE") + sys.path.append(os.path.dirname(config_file)) + importlib.import_module(os.path.splitext(os.path.basename(config_file))[0]) except: - raise Exception("Could not find the measurement file in the environment variables or the auspex globals.") - \ No newline at end of file + raise Exception(f"Could not import/execute the BBN_CONFIG_FILE {cfg}") + +def load_db(): + global db_file + if os.getenv('BBN_DB'): + db_file = os.getenv("BBN_DB") \ No newline at end of file From 10c7944499c884fda12a7603f83bf664c0ca4084 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 11 Jun 2018 21:22:28 -0400 Subject: [PATCH 041/235] Adding save/load functionality --- QGL/ChannelLibraries.py | 174 ++++++++++++++++++++++++---------------- QGL/Channels.py | 20 ++--- 2 files changed, 114 insertions(+), 80 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index fc4294fb..afd109cc 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -54,49 +54,60 @@ def set_from_dict(obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") -def copy_objs(chans, srcs): +def copy_objs(chans, srcs, new_channel_db): new_chans = [] new_srcs = [] old_to_new_chan = {} old_to_new_src = {} for chan in chans: - c = copy_entity(chan) - print("Copied", chan, "to", c) + c = copy_entity(chan, new_channel_db) new_chans.append(c) old_to_new_chan[chan] = c for src in srcs: - c = copy_entity(src) - print("Copied", src, "to", c) + c = copy_entity(src, new_channel_db) new_srcs.append(c) old_to_new_src[src] = c - # # Fix links... pony updates the relationships symmetriacally so we get some for free - # for thing in new_chans + new_srcs: - # print(f"Fixing {thing}") - # for attr in thing._attrs_: - # print(f"\t{attr}") - # if attr: - # if isinstance(getattr(thing, attr.name), Channels.Channel): - # if getattr(thing, attr.name) in old_to_new_chan.keys(): - # print(f"setting {thing} {attr.name} to {old_to_new_chan[getattr(thing, attr.name)]}") - # setattr(thing, attr.name, old_to_new_chan[getattr(thing, attr.name)]) - # elif isinstance(getattr(thing, attr.name), Channels.MicrowaveSource): - # if getattr(thing, attr.name) in old_to_new_src.keys(): - # print(f"setting {thing} {attr.name} to {old_to_new_src[getattr(thing, attr.name)]}") - # setattr(thing, attr.name, old_to_new_src[getattr(thing, attr.name)]) + # Fix links... pony updates the relationships symmetriacally so we get some for free + for thing in new_chans + new_srcs: + for attr in thing._attrs_: + if attr: + if isinstance(getattr(thing, attr.name), Channels.Channel): + if getattr(thing, attr.name) in old_to_new_chan.keys(): + setattr(thing, attr.name, old_to_new_chan[getattr(thing, attr.name)]) + elif isinstance(getattr(thing, attr.name), Channels.MicrowaveSource): + if getattr(thing, attr.name) in old_to_new_src.keys(): + setattr(thing, attr.name, old_to_new_src[getattr(thing, attr.name)]) return new_chans, new_srcs -def copy_entity(obj): +def copy_entity(obj, new_channel_db): """Copy a pony entity instance""" - kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_} - kwargs.pop("id") - # kwargs.pop("classtype") - kwargs.pop("channel_db") + kwargs = {a.name: getattr(obj, a.name) for a in obj.__class__._attrs_ if a.name not in ["id", "classtype", "pulse_params"]} + # if "pulse_params" in kwargs.keys(): + # kwargs["pulse_params"] = dict(kwargs["pulse_params"]) + kwargs["channel_db"] = new_channel_db return obj.__class__(**kwargs) +# def copy_entity(obj, new_channel_db): +# """Copy a pony entity instance""" +# attr_names = [a.name for a in obj.__class__._attrs_] +# skip = ["id", "classtype", "channel_db"] +# print(obj) +# kwargs = {"channel_db": new_channel_db} +# for a in attr_names: #["label", "source_type"]: +# print("\t", a) +# if a in dir(obj): +# val = getattr(obj, a) +# if a == "pulse_params": +# val = dict(val) +# if val is not None and a not in skip: +# print("\t\t", getattr(obj, a)) +# kwargs[a] = val +# return obj.__class__(**kwargs) + class ChannelLibrary(object): def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **kwargs): @@ -125,12 +136,12 @@ def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **k self.channelDict = {} self.channels = [] self.sources = [] - self.channelDatabase = None + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) self.channel_db_name = channel_db_name if channel_db_name else "temp" config.load_config() - self.load_most_recent() + # self.load_most_recent() # config.load_config() # Update the global reference @@ -138,7 +149,7 @@ def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **k channelLib = self def get_current_channels(self): - return list(select(c for c in Channels.Channel if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) + return list(select(c for c in Channels.Channel if c.channel_db is None)) + list(select(c for c in Channels.MicrowaveSource if c.channel_db is None)) def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} @@ -150,45 +161,68 @@ def load_by_id(self, id_num): obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() self.load(obj) - self.channels = list(obj.channels) - self.sources = list(obj.sources) - self.channel_db_name = obj.label + def clear(self): + select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) + select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) + self.channelDatabase.time = datetime.datetime.now() + + def load(self, obj): #, delete=True): + self.clear() - def load(self, obj): - self.channels = list(obj.channels) - self.sources = list(obj.sources) + chans = list(obj.channels) + srcs = list(obj.sources) + + # self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + new_chans, new_srcs = copy_objs(chans, srcs, self.channelDatabase) + + self.channels = new_chans + self.sources = new_srcs self.channel_db_name = obj.label - self.channelDatabase = obj - - def load_most_recent(self, name=None): - if name is None: - name = self.channel_db_name - mrcd = Channels.ChannelDatabase.select(lambda d: d.label==name).order_by(desc(Channels.ChannelDatabase.time)).first() - if mrcd: - self.load(mrcd) - - def new(self, name): - self.channelDatabase = None - self.channel_db_name = name - self.channels = [] - self.sources = [] + # self.channelDatabase = None + + # def load_most_recent(self, name=None): + # if name is None: + # name = self.channel_db_name + # mrcd = Channels.ChannelDatabase.select(lambda d: d.label==name).order_by(desc(Channels.ChannelDatabase.time)).first() + # if mrcd: + # self.load(mrcd) + + # def new(self, name): + # # self.channelDatabase.delete() + # self.clear() + # commit() + + # # self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + + # # self.channelDatabase = None + # self.channel_db_name = name + # self.channels = [] + # self.sources = [] def save(self): self.save_as(self.channel_db_name) def save_as(self, name): - # Get channels that are part of the currently active db, or find those that aren't yet part of a db - chans = list(select(c for c in Channels.Channel if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) - srcs = list(select(c for c in Channels.MicrowaveSource if (c.channel_db == self.channelDatabase) or (c.channel_db is None))) + # self.channelDatabase.label = name + # self.channelDatabase.time = datetime.datetime.now() + # cd = self.channelDatabase + # self.channelDatabase = None + # commit() + # self.load(cd, delete=False) - # chans, src = copy_objs(chans, srcs) - - cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now(), channels=chans, sources=srcs) - self.channels = chans - self.sources = srcs - self.channelDatabase = cd + # Get channels that are part of the currently active db + # chans = list(select(c for c in Channels.Channel if c.channel_db is None)) + # srcs = list(select(c for c in Channels.MicrowaveSource if c.channel_db is None)) + chans = list(self.channelDatabase.channels) + srcs = list(self.channelDatabase.sources) + cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now(), channels=chans, sources=srcs) + new_chans, new_srcs = copy_objs(chans, srcs, cd) + + # self.channels = new_chans + # self.sources = new_srcs + # self.channelDatabase = None commit() - self.channel_db_name = name + # self.channel_db_name = name #Dictionary methods def __getitem__(self, key): @@ -420,11 +454,11 @@ def load_from_library(self, return_only=False): class APS2(object): def __init__(self, label, address=None, delay=0.0): - self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern") - self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern") - self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern") - self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern") - self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern") + self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) self.trigger_interval = None self.trigger_source = "External" @@ -434,8 +468,8 @@ def __init__(self, label, address=None, delay=0.0): class X6(object): def __init__(self, label, address=None): - self.chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1") - self.chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2") + self.chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel_db=channelLib.channelDatabase) + self.chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel_db=channelLib.channelDatabase) self.address = address self.reference = "external" @@ -444,10 +478,10 @@ def __init__(self, label, address=None): self.acquire_mode = "digitizer" def new_qubit(label): - return Channels.Qubit(label=label) + return Channels.Qubit(label=label, channel_db=channelLib.channelDatabase) def new_source(label, source_type, address, power=-30.0): - return Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power) + return Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power, channel_db=channelLib.channelDatabase) def set_control(qubit, awg, generator=None): qubit.phys_chan = awg.chan12 @@ -455,10 +489,10 @@ def set_control(qubit, awg, generator=None): qubit.phys_chan.generator = generator def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=1, gate=False, gate_channel=2, trigger_length=1e-7): - meas = Channels.Measurement(label=f"M-{qubit.label}") + meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=channelLib.channelDatabase) meas.phys_chan = awg.chan12 - meas.trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}") + meas.trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=channelLib.channelDatabase) meas.trig_chan.phys_chan = getattr(awg, f"m{trig_channel}") meas.trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.receiver_chan = getattr(dig, f"chan{dig_channel}") @@ -467,11 +501,11 @@ def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=1, meas.phys_chan.generator = generator if gate: - meas.gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate") + meas.gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=channelLib.channelDatabase) meas.gate_chan.phys_chan = getattr(awg, f"m{gate_channel}") def set_master(awg, trig_channel=2, pulse_length=1e-7): - st = Channels.LogicalMarkerChannel(label="slave_trig") + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=channelLib.channelDatabase) st.phys_chan = getattr(awg, f"m{trig_channel}") st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} awg.master = True diff --git a/QGL/Channels.py b/QGL/Channels.py index 274a0d47..bf50ca71 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -47,8 +47,8 @@ def define_entities(db): class ChannelDatabase(db.Entity): label = Required(str) - channels = Set("Channel") - sources = Set("MicrowaveSource") + channels = Set("Channel", cascade_delete=True) + sources = Set("MicrowaveSource", cascade_delete=True) time = Optional(datetime.datetime) class MicrowaveSource(db.Entity): @@ -57,14 +57,14 @@ class MicrowaveSource(db.Entity): address = Optional(str) power = Optional(float) logical_channel = Optional("PhysicalChannel") - channel_db = Optional("ChannelDatabase") + channel_db = Required("ChannelDatabase") class Channel(db.Entity): ''' Every channel has a label and some printers. ''' label = Required(str) - channel_db = Optional("ChannelDatabase") + channel_db = Required("ChannelDatabase") def __repr__(self): return str(self) @@ -83,9 +83,9 @@ class PhysicalChannel(Channel): # Required reverse connections logical_channel = Optional("LogicalChannel") - quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") - quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") - marker_channel = Optional("PhysicalMarkerChannel") + # quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") + # quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") + # marker_channel = Optional("PhysicalMarkerChannel") class LogicalChannel(Channel): ''' @@ -108,14 +108,14 @@ class PhysicalMarkerChannel(PhysicalChannel): ''' gate_buffer = Required(float, default=0.0) gate_min_width = Required(float, default=0.0) - phys_channel = Optional(PhysicalChannel) + # phys_channel = Optional(PhysicalChannel) class PhysicalQuadratureChannel(PhysicalChannel): ''' Something used to implement a standard qubit channel with two analog channels and a microwave gating channel. ''' - I_channel = Optional(PhysicalChannel) - Q_channel = Optional(PhysicalChannel) + # I_channel = Optional(PhysicalChannel) + # Q_channel = Optional(PhysicalChannel) amp_factor = Required(float, default=1.0) phase_skew = Required(float, default=0.0) # marker_channel = Optional(PhysicalMarkerChannel) From 7f02f9f12816b2f8af334eaa3f23994090c71df9 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 11 Jun 2018 21:27:38 -0400 Subject: [PATCH 042/235] ChannelLibrary can now close and reopen db connections on instantiation --- QGL/ChannelLibraries.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index afd109cc..c7a4eca2 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -113,6 +113,10 @@ class ChannelLibrary(object): def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **kwargs): """Create the channel library.""" + global channelLib + if channelLib is not None: + channelLib.db.disconnect() + config.load_db() if database_file: self.database_file = database_file @@ -121,10 +125,10 @@ def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **k else: self.database_file = ":memory:" - db = Database() - Channels.define_entities(db) - db.bind('sqlite', filename=self.database_file, create_db=True) - db.generate_mapping(create_tables=True) + self.db = Database() + Channels.define_entities(self.db) + self.db.bind('sqlite', filename=self.database_file, create_db=True) + self.db.generate_mapping(create_tables=True) # Dirty trick: push the correct entity defs to the calling context for var in ["Measurement","Qubit","Edge"]: @@ -145,7 +149,6 @@ def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **k # config.load_config() # Update the global reference - global channelLib channelLib = self def get_current_channels(self): From 72d5c0bf982f811e28c572c849fe279f48ccfe44 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Jun 2018 14:14:48 -0400 Subject: [PATCH 043/235] Fixed copying and relinking --- QGL/ChannelLibraries.py | 150 +++++++++++++++++++--------------------- QGL/Channels.py | 4 +- QGL/config.py | 6 +- 3 files changed, 77 insertions(+), 83 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index c7a4eca2..ee0ce764 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -57,60 +57,76 @@ def set_from_dict(obj, settings): def copy_objs(chans, srcs, new_channel_db): new_chans = [] new_srcs = [] - old_to_new_chan = {} - old_to_new_src = {} + old_to_new = {} + links_to_change = {} + # old_to_new_chan = {} + # old_to_new_src = {} for chan in chans: - c = copy_entity(chan, new_channel_db) + c, links = copy_entity(chan, new_channel_db) new_chans.append(c) - old_to_new_chan[chan] = c + links_to_change[c] = links + #old_to_new[(c.id, c.label)] = c + old_to_new[c.label] = c for src in srcs: - c = copy_entity(src, new_channel_db) + c, links = copy_entity(src, new_channel_db) new_srcs.append(c) - old_to_new_src[src] = c - - # Fix links... pony updates the relationships symmetriacally so we get some for free - for thing in new_chans + new_srcs: - for attr in thing._attrs_: - if attr: - if isinstance(getattr(thing, attr.name), Channels.Channel): - if getattr(thing, attr.name) in old_to_new_chan.keys(): - setattr(thing, attr.name, old_to_new_chan[getattr(thing, attr.name)]) - elif isinstance(getattr(thing, attr.name), Channels.MicrowaveSource): - if getattr(thing, attr.name) in old_to_new_src.keys(): - setattr(thing, attr.name, old_to_new_src[getattr(thing, attr.name)]) + links_to_change[c] = links + #old_to_new[(c.id, c.label)] = c + old_to_new[c.label] = c + + for chan, link_info in links_to_change.items(): + print(f"Updating links for {chan}") + for attr_name, link_name in link_info.items(): + # link is an (id, label) tuple + new = old_to_new[link_name] + print(f"\tSetting {attr_name} for {link_name} to {new}") + setattr(chan, attr_name, new) + + # # Fix links... pony updates the relationships symmetrically so we get some for free + # for thing in new_chans + new_srcs: + # # print(f"Relinking {thing}") + # for attr in thing._attrs_: + # obj = getattr(thing, attr.name) + # if hasattr(obj, "id"): + # # print(f"\tChecking {attr.name}: {getattr(thing, attr.name)} {getattr(thing, attr.name).id}") + + # # if isinstance(obj, Channels.Channel) or isinstance(obj, Channels.MicrowaveSource): + # if (obj.id, obj.label) in old_to_new.keys(): + # # print(f"setting {thing} {attr.name} to {old_to_new[(obj.id, obj.label)]} (was {obj})") + # setattr(thing, attr.name, old_to_new[(obj.id, obj.label)]) + # # if isinstance(obj, Channels.Channel): + # # if obj in old_to_new.keys(): + # # setattr(thing, attr.name, old_to_new[obj]) + # # elif isinstance(obj, Channels.MicrowaveSource): + # # if obj in old_to_new.keys(): + # # setattr(thing, attr.name, old_to_new[obj]) return new_chans, new_srcs def copy_entity(obj, new_channel_db): """Copy a pony entity instance""" - kwargs = {a.name: getattr(obj, a.name) for a in obj.__class__._attrs_ if a.name not in ["id", "classtype", "pulse_params"]} - # if "pulse_params" in kwargs.keys(): - # kwargs["pulse_params"] = dict(kwargs["pulse_params"]) + kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_ if a.name not in ["id", "classtype"]} + + # Extract any links to other entities + links = {} + print(f"Copying {obj}") + for attr in obj._attrs_: + if attr.name not in ["channel_db"]: + obj_attr = getattr(obj, attr.name) + # print(f"\tChecking out {attr.name}") + if hasattr(obj_attr, "id"): + kwargs.pop(attr.name) + print(f"\tWill relink {attr.name} to {(obj_attr.id, obj_attr.label)}") + links[attr.name] = obj_attr.label #(obj_attr.id, obj_attr.label) + kwargs["channel_db"] = new_channel_db - return obj.__class__(**kwargs) - -# def copy_entity(obj, new_channel_db): -# """Copy a pony entity instance""" -# attr_names = [a.name for a in obj.__class__._attrs_] -# skip = ["id", "classtype", "channel_db"] -# print(obj) -# kwargs = {"channel_db": new_channel_db} -# for a in attr_names: #["label", "source_type"]: -# print("\t", a) -# if a in dir(obj): -# val = getattr(obj, a) -# if a == "pulse_params": -# val = dict(val) -# if val is not None and a not in skip: -# print("\t\t", getattr(obj, a)) -# kwargs[a] = val -# return obj.__class__(**kwargs) + return obj.__class__(**kwargs), links class ChannelLibrary(object): - def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **kwargs): + def __init__(self, database_file=None, channelDict={}, **kwargs): """Create the channel library.""" global channelLib @@ -140,8 +156,17 @@ def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **k self.channelDict = {} self.channels = [] self.sources = [] + + # Check to see whether there is already a temp database + # if "__temp__" in select(c.label for c in Channels.ChannelDatabase): + # for cdb in select(cdb for cdb in Channels.ChannelDatabase if cdb.label == "__temp__"): + # cdb.channels.clear() + # cdb.sources.clear() + # select(c for c in Channels.Channel if c.channel_db.label is None).delete(bulk=True) + # select(c for c in Channels.MicrowaveSource if c.channel_db.label is None).delete(bulk=True) + # select(c for c in Channels.ChannelDatabase if c.label=="__temp__").delete() + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) - self.channel_db_name = channel_db_name if channel_db_name else "temp" config.load_config() @@ -152,7 +177,7 @@ def __init__(self, channel_db_name=None, database_file=None, channelDict={}, **k channelLib = self def get_current_channels(self): - return list(select(c for c in Channels.Channel if c.channel_db is None)) + list(select(c for c in Channels.MicrowaveSource if c.channel_db is None)) + return list(select(c for c in Channels.Channel if c.channel_db == self.channelDatabase)) + list(select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase)) def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} @@ -175,57 +200,26 @@ def load(self, obj): #, delete=True): chans = list(obj.channels) srcs = list(obj.sources) - # self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) new_chans, new_srcs = copy_objs(chans, srcs, self.channelDatabase) self.channels = new_chans self.sources = new_srcs self.channel_db_name = obj.label - # self.channelDatabase = None - - # def load_most_recent(self, name=None): - # if name is None: - # name = self.channel_db_name - # mrcd = Channels.ChannelDatabase.select(lambda d: d.label==name).order_by(desc(Channels.ChannelDatabase.time)).first() - # if mrcd: - # self.load(mrcd) - - # def new(self, name): - # # self.channelDatabase.delete() - # self.clear() - # commit() - - # # self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) - - # # self.channelDatabase = None - # self.channel_db_name = name - # self.channels = [] - # self.sources = [] def save(self): self.save_as(self.channel_db_name) def save_as(self, name): - # self.channelDatabase.label = name - # self.channelDatabase.time = datetime.datetime.now() - # cd = self.channelDatabase - # self.channelDatabase = None - # commit() - # self.load(cd, delete=False) - - # Get channels that are part of the currently active db - # chans = list(select(c for c in Channels.Channel if c.channel_db is None)) - # srcs = list(select(c for c in Channels.MicrowaveSource if c.channel_db is None)) chans = list(self.channelDatabase.channels) srcs = list(self.channelDatabase.sources) - cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now(), channels=chans, sources=srcs) - new_chans, new_srcs = copy_objs(chans, srcs, cd) + commit() - # self.channels = new_chans - # self.sources = new_srcs - # self.channelDatabase = None + cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now()) + new_chans, new_srcs = copy_objs(chans, srcs, cd) + cd.channels = new_chans + cd.sources = new_srcs commit() - # self.channel_db_name = name + #Dictionary methods def __getitem__(self, key): diff --git a/QGL/Channels.py b/QGL/Channels.py index bf50ca71..f503bd69 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -57,14 +57,14 @@ class MicrowaveSource(db.Entity): address = Optional(str) power = Optional(float) logical_channel = Optional("PhysicalChannel") - channel_db = Required("ChannelDatabase") + channel_db = Optional("ChannelDatabase") class Channel(db.Entity): ''' Every channel has a label and some printers. ''' label = Required(str) - channel_db = Required("ChannelDatabase") + channel_db = Optional("ChannelDatabase") def __repr__(self): return str(self) diff --git a/QGL/config.py b/QGL/config.py index 085c48d8..eaeb6322 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -27,13 +27,13 @@ def load_config(): global config_file - if os.getenv('BBN_CONFIG_FILE'): + if os.getenv('BBN_CONFIG'): try: - config_file = os.getenv("BBN_CONFIG_FILE") + config_file = os.getenv("BBN_CONFIG") sys.path.append(os.path.dirname(config_file)) importlib.import_module(os.path.splitext(os.path.basename(config_file))[0]) except: - raise Exception(f"Could not import/execute the BBN_CONFIG_FILE {cfg}") + raise Exception(f"Could not import/execute the BBN_CONFIG {os.getenv('BBN_CONFIG')}") def load_db(): global db_file From 02f777ab2705cdbe47715822248cef2e04537aaf Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Jun 2018 15:50:47 -0400 Subject: [PATCH 044/235] Deletion working more reliably --- QGL/ChannelLibraries.py | 71 ++++++++++++++++++++--------------------- 1 file changed, 35 insertions(+), 36 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index ee0ce764..f981f7f1 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -77,11 +77,11 @@ def copy_objs(chans, srcs, new_channel_db): old_to_new[c.label] = c for chan, link_info in links_to_change.items(): - print(f"Updating links for {chan}") + # print(f"Updating links for {chan}") for attr_name, link_name in link_info.items(): # link is an (id, label) tuple new = old_to_new[link_name] - print(f"\tSetting {attr_name} for {link_name} to {new}") + # print(f"\tSetting {attr_name} for {link_name} to {new}") setattr(chan, attr_name, new) # # Fix links... pony updates the relationships symmetrically so we get some for free @@ -111,14 +111,14 @@ def copy_entity(obj, new_channel_db): # Extract any links to other entities links = {} - print(f"Copying {obj}") + # print(f"Copying {obj}") for attr in obj._attrs_: if attr.name not in ["channel_db"]: obj_attr = getattr(obj, attr.name) # print(f"\tChecking out {attr.name}") if hasattr(obj_attr, "id"): kwargs.pop(attr.name) - print(f"\tWill relink {attr.name} to {(obj_attr.id, obj_attr.label)}") + # print(f"\tWill relink {attr.name} to {(obj_attr.id, obj_attr.label)}") links[attr.name] = obj_attr.label #(obj_attr.id, obj_attr.label) kwargs["channel_db"] = new_channel_db @@ -154,30 +154,22 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): # This is still somewhere legacy QGL behavior. Massage db into dict for lookup. self.channelDict = {} - self.channels = [] - self.sources = [] - # Check to see whether there is already a temp database - # if "__temp__" in select(c.label for c in Channels.ChannelDatabase): - # for cdb in select(cdb for cdb in Channels.ChannelDatabase if cdb.label == "__temp__"): - # cdb.channels.clear() - # cdb.sources.clear() - # select(c for c in Channels.Channel if c.channel_db.label is None).delete(bulk=True) - # select(c for c in Channels.MicrowaveSource if c.channel_db.label is None).delete(bulk=True) - # select(c for c in Channels.ChannelDatabase if c.label=="__temp__").delete() - - self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + Check to see whether there is already a temp database + if "__temp__" in select(c.label for c in Channels.ChannelDatabase): + self.channelDatabase = select(c.label for c in Channels.ChannelDatabase).first() + self.clear() + else: + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) config.load_config() - - # self.load_most_recent() - # config.load_config() - + # Update the global reference channelLib = self def get_current_channels(self): - return list(select(c for c in Channels.Channel if c.channel_db == self.channelDatabase)) + list(select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase)) + return list(self.channelDatabase.channels) + list(self.channelDatabase.sources) + # return list(select(c for c in Channels.Channel if c.channel_db == self.channelDatabase)) + list(select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase)) def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} @@ -190,9 +182,14 @@ def load_by_id(self, id_num): self.load(obj) def clear(self): - select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) + select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) + self.channelDatabase.time = datetime.datetime.now() + commit() + # select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) + # select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) + # self.channelDatabase.time = datetime.datetime.now() def load(self, obj): #, delete=True): self.clear() @@ -202,12 +199,12 @@ def load(self, obj): #, delete=True): new_chans, new_srcs = copy_objs(chans, srcs, self.channelDatabase) - self.channels = new_chans - self.sources = new_srcs - self.channel_db_name = obj.label - def save(self): - self.save_as(self.channel_db_name) + # self.channelDatabase.label = obj.label + # self.channel_db_name = obj.label + + # def save(self): + # self.save_as(self.channel_db_name) def save_as(self, name): chans = list(self.channelDatabase.channels) @@ -487,19 +484,21 @@ def set_control(qubit, awg, generator=None): def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=1, gate=False, gate_channel=2, trigger_length=1e-7): meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=channelLib.channelDatabase) + if generator: + awg.chan12.generator = generator meas.phys_chan = awg.chan12 - meas.trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=channelLib.channelDatabase) - meas.trig_chan.phys_chan = getattr(awg, f"m{trig_channel}") - meas.trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} - meas.receiver_chan = getattr(dig, f"chan{dig_channel}") - - if generator: - meas.phys_chan.generator = generator + trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=channelLib.channelDatabase) + trig_chan.phys_chan = getattr(awg, f"m{trig_channel}") + trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} + meas.trig_chan = trig_chan + + meas.receiver_chan = getattr(dig, f"chan{dig_channel}") if gate: - meas.gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=channelLib.channelDatabase) - meas.gate_chan.phys_chan = getattr(awg, f"m{gate_channel}") + gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=channelLib.channelDatabase) + gate_chan.phys_chan = getattr(awg, f"m{gate_channel}") + meas.gate_chan = gate_chan def set_master(awg, trig_channel=2, pulse_length=1e-7): st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=channelLib.channelDatabase) From a3506715975b418c26dbfbc4e0b5da38aaa84214 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Jun 2018 16:26:44 -0400 Subject: [PATCH 045/235] Fix initial loading --- QGL/ChannelLibraries.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index f981f7f1..fbfe589d 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -155,15 +155,15 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): # This is still somewhere legacy QGL behavior. Massage db into dict for lookup. self.channelDict = {} - Check to see whether there is already a temp database + # Check to see whether there is already a temp database if "__temp__" in select(c.label for c in Channels.ChannelDatabase): - self.channelDatabase = select(c.label for c in Channels.ChannelDatabase).first() + self.channelDatabase = select(c for c in Channels.ChannelDatabase if c.label == "__temp__").first() self.clear() else: self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) config.load_config() - + # Update the global reference channelLib = self From 5ccb57e3c27668e90f48e4a0f79461264d1c06fc Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Jun 2018 16:54:39 -0400 Subject: [PATCH 046/235] Fixed deletion of additional temporary channel dbs --- QGL/ChannelLibraries.py | 58 +++++------------------------------------ 1 file changed, 7 insertions(+), 51 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index fbfe589d..deba7f20 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -59,50 +59,24 @@ def copy_objs(chans, srcs, new_channel_db): new_srcs = [] old_to_new = {} links_to_change = {} - # old_to_new_chan = {} - # old_to_new_src = {} for chan in chans: c, links = copy_entity(chan, new_channel_db) new_chans.append(c) links_to_change[c] = links - #old_to_new[(c.id, c.label)] = c old_to_new[c.label] = c for src in srcs: c, links = copy_entity(src, new_channel_db) new_srcs.append(c) links_to_change[c] = links - #old_to_new[(c.id, c.label)] = c old_to_new[c.label] = c for chan, link_info in links_to_change.items(): - # print(f"Updating links for {chan}") for attr_name, link_name in link_info.items(): - # link is an (id, label) tuple new = old_to_new[link_name] - # print(f"\tSetting {attr_name} for {link_name} to {new}") setattr(chan, attr_name, new) - # # Fix links... pony updates the relationships symmetrically so we get some for free - # for thing in new_chans + new_srcs: - # # print(f"Relinking {thing}") - # for attr in thing._attrs_: - # obj = getattr(thing, attr.name) - # if hasattr(obj, "id"): - # # print(f"\tChecking {attr.name}: {getattr(thing, attr.name)} {getattr(thing, attr.name).id}") - - # # if isinstance(obj, Channels.Channel) or isinstance(obj, Channels.MicrowaveSource): - # if (obj.id, obj.label) in old_to_new.keys(): - # # print(f"setting {thing} {attr.name} to {old_to_new[(obj.id, obj.label)]} (was {obj})") - # setattr(thing, attr.name, old_to_new[(obj.id, obj.label)]) - # # if isinstance(obj, Channels.Channel): - # # if obj in old_to_new.keys(): - # # setattr(thing, attr.name, old_to_new[obj]) - # # elif isinstance(obj, Channels.MicrowaveSource): - # # if obj in old_to_new.keys(): - # # setattr(thing, attr.name, old_to_new[obj]) - return new_chans, new_srcs def copy_entity(obj, new_channel_db): @@ -111,15 +85,12 @@ def copy_entity(obj, new_channel_db): # Extract any links to other entities links = {} - # print(f"Copying {obj}") for attr in obj._attrs_: if attr.name not in ["channel_db"]: obj_attr = getattr(obj, attr.name) - # print(f"\tChecking out {attr.name}") if hasattr(obj_attr, "id"): kwargs.pop(attr.name) - # print(f"\tWill relink {attr.name} to {(obj_attr.id, obj_attr.label)}") - links[attr.name] = obj_attr.label #(obj_attr.id, obj_attr.label) + links[attr.name] = obj_attr.label kwargs["channel_db"] = new_channel_db return obj.__class__(**kwargs), links @@ -157,10 +128,8 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): # Check to see whether there is already a temp database if "__temp__" in select(c.label for c in Channels.ChannelDatabase): - self.channelDatabase = select(c for c in Channels.ChannelDatabase if c.label == "__temp__").first() - self.clear() - else: - self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + select(d for d in Channels.ChannelDatabase if d.label == "__temp__").delete(bulk=True) + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) config.load_config() @@ -169,7 +138,6 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): def get_current_channels(self): return list(self.channelDatabase.channels) + list(self.channelDatabase.sources) - # return list(select(c for c in Channels.Channel if c.channel_db == self.channelDatabase)) + list(select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase)) def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} @@ -184,40 +152,28 @@ def load_by_id(self, id_num): def clear(self): select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) - self.channelDatabase.time = datetime.datetime.now() commit() - # select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) - # select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) - # self.channelDatabase.time = datetime.datetime.now() + select(d for d in Channels.ChannelDatabase if d.label == "__temp__").delete(bulk=True) + commit() + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) - def load(self, obj): #, delete=True): + def load(self, obj): self.clear() - chans = list(obj.channels) srcs = list(obj.sources) - new_chans, new_srcs = copy_objs(chans, srcs, self.channelDatabase) - - # self.channelDatabase.label = obj.label - # self.channel_db_name = obj.label - - # def save(self): - # self.save_as(self.channel_db_name) - def save_as(self, name): chans = list(self.channelDatabase.channels) srcs = list(self.channelDatabase.sources) commit() - cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now()) new_chans, new_srcs = copy_objs(chans, srcs, cd) cd.channels = new_chans cd.sources = new_srcs commit() - #Dictionary methods def __getitem__(self, key): return self.channelDict[key] From 45e37a97305b5f63acc1dc630d46b3cb4d6088e4 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 13 Jun 2018 10:09:29 -0400 Subject: [PATCH 047/235] Clean up loading and purging temp channel dbs --- QGL/ChannelLibraries.py | 41 +++++++++++++++++++++++++++++++++++------ QGL/PulseSequencer.py | 7 +++++++ 2 files changed, 42 insertions(+), 6 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index deba7f20..b24533f9 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -97,7 +97,7 @@ def copy_entity(obj, new_channel_db): class ChannelLibrary(object): - def __init__(self, database_file=None, channelDict={}, **kwargs): + def __init__(self, database_file=None, channelDict={}, auto_update=True, **kwargs): """Create the channel library.""" global channelLib @@ -128,11 +128,22 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): # Check to see whether there is already a temp database if "__temp__" in select(c.label for c in Channels.ChannelDatabase): - select(d for d in Channels.ChannelDatabase if d.label == "__temp__").delete(bulk=True) + for cdb in select(d for d in Channels.ChannelDatabase if d.label == "__temp__"): + print("Trying to clear", cdb.label) + select(c for c in Channels.MicrowaveSource if c.channel_db == cdb).delete(bulk=True) + select(c for c in Channels.Channel if c.channel_db == cdb).delete(bulk=True) + commit() + select(d for d in Channels.ChannelDatabase if d == cdb).delete(bulk=True) + commit() + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) config.load_config() + # Whether or not we try to keep qubitfactories up to date + self.auto_update = auto_update + self.blub = {} + # Update the global reference channelLib = self @@ -143,11 +154,16 @@ def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} def list(self): - select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).show() + select((c.label, c.time, c.id) for c in Channels.ChannelDatabase if c.label != "__temp__").sort_by(1, 2).show() + + def load(self, name, index=1): + """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ + obj = list(select(c for c in Channels.ChannelDatabase if c.label==name).sort_by(desc(Channels.ChannelDatabase.time))) + self.load_obj(obj[-index]) def load_by_id(self, id_num): obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() - self.load(obj) + self.load_obj(obj) def clear(self): select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) @@ -158,12 +174,23 @@ def clear(self): commit() self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) - def load(self, obj): + def load_obj(self, obj): + commit() self.clear() chans = list(obj.channels) srcs = list(obj.sources) new_chans, new_srcs = copy_objs(chans, srcs, self.channelDatabase) + self.update_channelDict() + + # # Try to fix factories + if self.auto_update: + for k in list(self.blub.keys()): + self.blub[k] = self.channelDict[k] + # for k,v in globals().items(): + # if isinstance(v, Channels.Qubit): + # globals()[k] = self.channelDict[k] + def save_as(self, name): chans = list(self.channelDatabase.channels) srcs = list(self.channelDatabase.sources) @@ -468,8 +495,10 @@ def QubitFactory(label, **kwargs): # TODO: this will just get the first entry in the whole damned DB! # thing = select(el for el in Channels.Qubit if el.label==label).first() thing = {c.label: c for c in channelLib.get_current_channels()}[label] + if thing: - return thing + channelLib.blub[label] = thing + return channelLib.blub[label] else: return Channels.Qubit(label=label, **kwargs) diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 7b79d9af..34d1f07f 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -280,7 +280,14 @@ def flatten_to_pulses(obj): for pulse in obj.pulses.values(): yield from flatten_to_pulses(pulse) + def rec_length(obj): + if hasattr(obj, "length"): + return obj.length + else: + return rec_length(obj[0]) + pulse_lengths = np.array([pulse.length for pulse in pulses]) + # pulse_lengths = np.array([rec_length(pulse) for pulse in pulses]) pad_lengths = max(pulse_lengths) - pulse_lengths pulse_list = [] for k,pulse in enumerate(pulses): From b83b0b1af8504af5c7f44078e5a3dd247e2fead1 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 13 Jun 2018 21:02:07 -0400 Subject: [PATCH 048/235] Strip out autoupdate for now --- QGL/ChannelLibraries.py | 18 ++---------------- 1 file changed, 2 insertions(+), 16 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index b24533f9..3b37ed05 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -97,7 +97,7 @@ def copy_entity(obj, new_channel_db): class ChannelLibrary(object): - def __init__(self, database_file=None, channelDict={}, auto_update=True, **kwargs): + def __init__(self, database_file=None, channelDict={}, **kwargs): """Create the channel library.""" global channelLib @@ -140,10 +140,6 @@ def __init__(self, database_file=None, channelDict={}, auto_update=True, **kwarg config.load_config() - # Whether or not we try to keep qubitfactories up to date - self.auto_update = auto_update - self.blub = {} - # Update the global reference channelLib = self @@ -183,14 +179,6 @@ def load_obj(self, obj): self.update_channelDict() - # # Try to fix factories - if self.auto_update: - for k in list(self.blub.keys()): - self.blub[k] = self.channelDict[k] - # for k,v in globals().items(): - # if isinstance(v, Channels.Qubit): - # globals()[k] = self.channelDict[k] - def save_as(self, name): chans = list(self.channelDatabase.channels) srcs = list(self.channelDatabase.sources) @@ -495,10 +483,8 @@ def QubitFactory(label, **kwargs): # TODO: this will just get the first entry in the whole damned DB! # thing = select(el for el in Channels.Qubit if el.label==label).first() thing = {c.label: c for c in channelLib.get_current_channels()}[label] - if thing: - channelLib.blub[label] = thing - return channelLib.blub[label] + return thing else: return Channels.Qubit(label=label, **kwargs) From 6df5c9ea0d2ec214cb924f5c13a3f03d14cd2b07 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 15 Jun 2018 15:34:19 -0400 Subject: [PATCH 049/235] Starting to add instrument entities to db --- QGL/ChannelLibraries.py | 118 +++++++++++++++++++++++++++++----------- QGL/Channels.py | 61 +++++++++++++++++---- QGL/__init__.py | 4 +- 3 files changed, 139 insertions(+), 44 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 3b37ed05..1eeac1d4 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -417,58 +417,112 @@ def load_from_library(self, return_only=False): # Convenience functions for generating and linking channels # TODO: move these to a shim layer shared by Auspex/QGL -class APS2(object): - def __init__(self, label, address=None, delay=0.0): - self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) +# class APS2(object): +# def __init__(self, label, address=None, delay=0.0): +# self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) +# self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) +# self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) +# self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) +# self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - self.trigger_interval = None - self.trigger_source = "External" - self.address = address - self.delay = delay - self.master = False - -class X6(object): - def __init__(self, label, address=None): - self.chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel_db=channelLib.channelDatabase) - self.chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel_db=channelLib.channelDatabase) - - self.address = address - self.reference = "external" - self.nbr_segments = 1 - self.nbr_round_robins = 100 - self.acquire_mode = "digitizer" +# self.trigger_interval = None +# self.trigger_source = "External" +# self.address = address +# self.delay = delay +# self.master = False + +def new_APS2(label, address): + chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="new_APS2Pattern", channel_db=channelLib.channelDatabase) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) + + this_awg = Channels.AWG(label=label, address=address, channels=[chan12, m1, m2, m3, m4]) + this_awg.trigger_source = "External" + this_awg.address = address + + commit() + return this_awg + +def new_X6(label, address): + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel_db=channelLib.channelDatabase) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel_db=channelLib.channelDatabase) + + this_dig = Channels.Digitizer(label=label, address=address, channels=[chan1, chan2]) + this_dig.trigger_source = "External" + this_dig.address = address + + commit() + return this_dig def new_qubit(label): - return Channels.Qubit(label=label, channel_db=channelLib.channelDatabase) + thing = Channels.Qubit(label=label, channel_db=channelLib.channelDatabase) + return thing def new_source(label, source_type, address, power=-30.0): - return Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power, channel_db=channelLib.channelDatabase) + thing = Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power, channel_db=channelLib.channelDatabase) + return thing def set_control(qubit, awg, generator=None): - qubit.phys_chan = awg.chan12 + quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + + if isinstance(awg, Channels.AWG) and len(quads) > 1: + raise ValueError("In set_control the AWG must have a single quadrature channel or a specific channel must be passed instead") + elif isinstance(awg, Channels.AWG) and len(quads) == 1: + phys_chan = quads[0] + elif isinstance(awg, Channels.PhysicalQuadratureChannel): + phys_chan = awg + else: + raise ValueError("In set_control the AWG must have a single quadrature channel or a specific channel must be passed instead") + + qubit.phys_chan = phys_chan if generator: qubit.phys_chan.generator = generator -def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=1, gate=False, gate_channel=2, trigger_length=1e-7): +def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): + print(qubit, awg, dig) + + quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + + if isinstance(awg, Channels.AWG) and len(quads) > 1: + raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") + elif isinstance(awg, Channels.AWG) and len(quads) == 1: + phys_chan = quads[0] + elif isinstance(awg, Channels.PhysicalQuadratureChannel): + phys_chan = awg + else: + raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") + meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=channelLib.channelDatabase) + meas.phys_chan = phys_chan if generator: - awg.chan12.generator = generator - meas.phys_chan = awg.chan12 + meas.phys_chan.generator = generator + phys_trig_channel = trig_channel if trig_channel else awg.get_chan("12m1") + trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=channelLib.channelDatabase) - trig_chan.phys_chan = getattr(awg, f"m{trig_channel}") + trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan - meas.receiver_chan = getattr(dig, f"chan{dig_channel}") + if isinstance(dig, Channels.Digitizer) and len(dig.channels) > 1: + raise ValueError("In set_measure the Digitizer must have a single receiver channel or a specific channel must be passed instead") + elif isinstance(dig, Channels.Digitizer) and len(dig.channels) == 1: + rcv_chan = dig.channels[0] + elif isinstance(dig, Channels.ReceiverChannel): + rcv_chan = dig + else: + raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") + + meas.receiver_chan = rcv_chan if gate: + phys_gate_channel = gate_channel if gate_channel else awg.get_chan("12m2") gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=channelLib.channelDatabase) - gate_chan.phys_chan = getattr(awg, f"m{gate_channel}") + gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan def set_master(awg, trig_channel=2, pulse_length=1e-7): diff --git a/QGL/Channels.py b/QGL/Channels.py index f503bd69..64fe2910 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -42,14 +42,18 @@ Edge = None MicrowaveSource = None ChannelDatabase = None +Digitizer = None +AWG = None def define_entities(db): class ChannelDatabase(db.Entity): - label = Required(str) - channels = Set("Channel", cascade_delete=True) - sources = Set("MicrowaveSource", cascade_delete=True) - time = Optional(datetime.datetime) + label = Required(str) + channels = Set("Channel", cascade_delete=True) + sources = Set("MicrowaveSource", cascade_delete=True) + awgs = Set("AWG", cascade_delete=True) + digitizers = Set("Digitizer", cascade_delete=True) + time = Optional(datetime.datetime) class MicrowaveSource(db.Entity): label = Required(str) @@ -59,6 +63,33 @@ class MicrowaveSource(db.Entity): logical_channel = Optional("PhysicalChannel") channel_db = Optional("ChannelDatabase") + class Digitizer(db.Entity): + label = Required(str) + address = Optional(str) + # stream_types = Set(str) + channels = Set("ReceiverChannel") + trigger_source = Required(str, default="External", py_check=lambda x: x in ['External', 'Internal']) + channel_db = Optional("ChannelDatabase") + # nbr_segments = Required(int, default=1) # This should be automatic + # nbr_round_robins = Required(int, default=100) # This should be automatic + # acquire_mode = Required(str,default="digitizer", py_check=lambda x: x in ['digitizer', 'averager']) + + def get_chan(self, name): + return self.channels.select(lambda x: x.label.endswith(name)).first() + + class AWG(db.Entity): + label = Required(str) + address = Optional(str) + channels = Set("PhysicalChannel", reverse="awg") + trigger_interval = Required(float, default=100e-6) + trigger_source = Required(str, default="External", py_check=lambda x: x in ['External', 'Internal']) + delay = Required(float, default=0.0) + master = Required(bool, default=False) + channel_db = Optional("ChannelDatabase") + + def get_chan(self, name): + return self.channels.select(lambda x: x.label.endswith(name)).first() + class Channel(db.Entity): ''' Every channel has a label and some printers. @@ -86,6 +117,7 @@ class PhysicalChannel(Channel): # quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") # quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") # marker_channel = Optional("PhysicalMarkerChannel") + awg = Optional(AWG) class LogicalChannel(Channel): ''' @@ -114,11 +146,12 @@ class PhysicalQuadratureChannel(PhysicalChannel): ''' Something used to implement a standard qubit channel with two analog channels and a microwave gating channel. ''' - # I_channel = Optional(PhysicalChannel) - # Q_channel = Optional(PhysicalChannel) - amp_factor = Required(float, default=1.0) - phase_skew = Required(float, default=0.0) - # marker_channel = Optional(PhysicalMarkerChannel) + amp_factor = Required(float, default=1.0) + phase_skew = Required(float, default=0.0) + I_channel_offset = Required(float, default=0.0) + Q_channel_offset = Required(float, default=0.0) + I_channel_amp_factor = Required(float, default=1.0) + Q_channel_amp_factor = Required(float, default=1.0) class ReceiverChannel(PhysicalChannel): ''' @@ -126,6 +159,13 @@ class ReceiverChannel(PhysicalChannel): ''' triggering_channel = Optional(LogicalChannel) channel = Optional(int) + stream_type = Required(str, default="Raw", py_check=lambda x: x.lower() in["raw", "demodulated", "integrated", "averaged"]) + if_freq = Required(float, default=0.0) + kernel = Optional(str) + kernel_bias = Required(float, default=0.0) + threshold = Required(float, default=0.0) + threshold_invert = Required(bool, default=False) + digitizer = Optional(Digitizer) def pulse_check(name): return name in ["constant", "gaussian", "drag", "gaussOn", "gaussOff", "dragGaussOn", "dragGaussOff", @@ -220,4 +260,5 @@ def __init__(self, **kwargs): globals()["Edge"] = Edge globals()["MicrowaveSource"] = MicrowaveSource globals()["ChannelDatabase"] = ChannelDatabase - \ No newline at end of file + globals()["Digitizer"] = Digitizer + globals()["AWG"] = AWG \ No newline at end of file diff --git a/QGL/__init__.py b/QGL/__init__.py index e11ea869..eca7769b 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -1,5 +1,5 @@ -from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase -from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib, APS2, X6, new_qubit, set_control, set_measure, set_master, new_source +from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase, AWG, Digitizer +from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib, new_APS2, new_X6, new_qubit, set_control, set_measure, set_master, new_source from .PulsePrimitives import * from .Compiler import compile_to_hardware, set_log_level from .PulseSequencer import align From e1c99acb94085476538bf9709eb6c9ecf97063f6 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 20 Jun 2018 06:19:42 -0400 Subject: [PATCH 050/235] Tweaks to clearing, convenience functions, etc. --- QGL/ChannelLibraries.py | 148 +++++++++++++++++++++++----------------- QGL/Channels.py | 20 ++++++ 2 files changed, 105 insertions(+), 63 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 1eeac1d4..55e9be21 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -54,30 +54,30 @@ def set_from_dict(obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") -def copy_objs(chans, srcs, new_channel_db): - new_chans = [] - new_srcs = [] +def copy_objs(*entities, new_channel_db): + # Entities is a list of lists of entities of specific types + new_entities = [] old_to_new = {} links_to_change = {} - for chan in chans: - c, links = copy_entity(chan, new_channel_db) - new_chans.append(c) - links_to_change[c] = links - old_to_new[c.label] = c - - for src in srcs: - c, links = copy_entity(src, new_channel_db) - new_srcs.append(c) - links_to_change[c] = links - old_to_new[c.label] = c + for ent in entities: + new_ents = [] + for obj in ent: + c, links = copy_entity(obj, new_channel_db) + new_ents.append(c) + links_to_change[c] = links + old_to_new[c.label] = c + new_entities.append(new_ents) for chan, link_info in links_to_change.items(): for attr_name, link_name in link_info.items(): - new = old_to_new[link_name] + if isinstance(link_name, pony.orm.core.Multiset): + new = [old_to_new[ln] for ln in link_name] + else: + new = old_to_new[link_name] setattr(chan, attr_name, new) - return new_chans, new_srcs + return new_entities def copy_entity(obj, new_channel_db): """Copy a pony entity instance""" @@ -127,16 +127,11 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): self.channelDict = {} # Check to see whether there is already a temp database - if "__temp__" in select(c.label for c in Channels.ChannelDatabase): - for cdb in select(d for d in Channels.ChannelDatabase if d.label == "__temp__"): - print("Trying to clear", cdb.label) - select(c for c in Channels.MicrowaveSource if c.channel_db == cdb).delete(bulk=True) - select(c for c in Channels.Channel if c.channel_db == cdb).delete(bulk=True) - commit() - select(d for d in Channels.ChannelDatabase if d == cdb).delete(bulk=True) - commit() + for cdb in select(d for d in Channels.ChannelDatabase if d.label == "__temp__"): + self.clear(channel_db=cdb, create_new=False) self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + commit() config.load_config() @@ -149,9 +144,40 @@ def get_current_channels(self): def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} - def list(self): + def ls(self): select((c.label, c.time, c.id) for c in Channels.ChannelDatabase if c.label != "__temp__").sort_by(1, 2).show() + def ent_by_type_name(self, name, show=False): + q = select(c for c in getattr(Channels,name) if c.label != "__temp__") + if show: + select(c.label for c in getattr(Channels,name) if c.label != "__temp__").sort_by(1).show() + else: + return {el.label: el for el in q} + + def dig(self): + return self.ent_by_type_name("Digitizer") + + def awg(self): + return self.ent_by_type_name("AWG") + + def qubit(self): + return self.ent_by_type_name("Qubit") + + def meas(self): + return self.ent_by_type_name("Measurement") + + def ls_dig(self): + return self.ent_by_type_name("Digitizer", show=True) + + def ls_awg(self): + return self.ent_by_type_name("AWG", show=True) + + def ls_qubit(self): + return self.ent_by_type_name("Qubit", show=True) + + def ls_meas(self): + return self.ent_by_type_name("Measurement", show=True) + def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ obj = list(select(c for c in Channels.ChannelDatabase if c.label==name).sort_by(desc(Channels.ChannelDatabase.time))) @@ -161,32 +187,36 @@ def load_by_id(self, id_num): obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() self.load_obj(obj) - def clear(self): - select(c for c in Channels.MicrowaveSource if c.channel_db == self.channelDatabase).delete(bulk=True) - select(c for c in Channels.Channel if c.channel_db == self.channelDatabase).delete(bulk=True) - self.channelDatabase.time = datetime.datetime.now() - commit() - select(d for d in Channels.ChannelDatabase if d.label == "__temp__").delete(bulk=True) - commit() - self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + def clear(self, channel_db=None, create_new=True): + # If no database is specified, clear self.database + channel_db = channel_db if channel_db else self.channelDatabase + # First clear items that don't have Sets of other items + for ent in [Channels.MicrowaveSource, Channels.Channel, Channels.AWG, Channels.Digitizer]: + select(c for c in ent if c.channel_db == channel_db).delete(bulk=True) + commit() + # Now clear items that do potentially have sets of items (which should be deleted) + for ent in [Channels.ChannelDatabase]: + select(d for d in ent if d.label == "__temp__").delete(bulk=True) + commit() + if create_new: + self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + commit() def load_obj(self, obj): commit() self.clear() - chans = list(obj.channels) - srcs = list(obj.sources) - new_chans, new_srcs = copy_objs(chans, srcs, self.channelDatabase) - + chans, srcs, awgs, digs = map(list, [obj.channels, obj.sources, obj.awgs, obj.digitizers]) + copy_objs(chans, srcs, awgs, digs, new_channel_db=self.channelDatabase) + commit() self.update_channelDict() def save_as(self, name): - chans = list(self.channelDatabase.channels) - srcs = list(self.channelDatabase.sources) + chans, srcs, awgs, digs = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, + self.channelDatabase.awgs, self.channelDatabase.digitizers]) commit() cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now()) - new_chans, new_srcs = copy_objs(chans, srcs, cd) - cd.channels = new_chans - cd.sources = new_srcs + new_chans, new_srcs, new_awgs, new_digs = copy_objs(chans, srcs, awgs, digs, new_channel_db=cd) + cd.channels, cd.sources, cd.awgs, cd.digitizers = new_chans, new_srcs, new_awgs, new_digs commit() #Dictionary methods @@ -417,28 +447,14 @@ def load_from_library(self, return_only=False): # Convenience functions for generating and linking channels # TODO: move these to a shim layer shared by Auspex/QGL -# class APS2(object): -# def __init__(self, label, address=None, delay=0.0): -# self.chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) -# self.m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) -# self.m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) -# self.m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) -# self.m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - -# self.trigger_interval = None -# self.trigger_source = "External" -# self.address = address -# self.delay = delay -# self.master = False - def new_APS2(label, address): - chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="new_APS2Pattern", channel_db=channelLib.channelDatabase) + chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - this_awg = Channels.AWG(label=label, address=address, channels=[chan12, m1, m2, m3, m4]) + this_awg = Channels.AWG(label=label, address=address, channels=[chan12, m1, m2, m3, m4], channel_db=channelLib.channelDatabase) this_awg.trigger_source = "External" this_awg.address = address @@ -449,7 +465,7 @@ def new_X6(label, address): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel_db=channelLib.channelDatabase) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel_db=channelLib.channelDatabase) - this_dig = Channels.Digitizer(label=label, address=address, channels=[chan1, chan2]) + this_dig = Channels.Digitizer(label=label, address=address, channels=[chan1, chan2], channel_db=channelLib.channelDatabase) this_dig.trigger_source = "External" this_dig.address = address @@ -458,10 +474,12 @@ def new_X6(label, address): def new_qubit(label): thing = Channels.Qubit(label=label, channel_db=channelLib.channelDatabase) + commit() return thing def new_source(label, source_type, address, power=-30.0): thing = Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power, channel_db=channelLib.channelDatabase) + commit() return thing def set_control(qubit, awg, generator=None): @@ -480,10 +498,9 @@ def set_control(qubit, awg, generator=None): qubit.phys_chan = phys_chan if generator: qubit.phys_chan.generator = generator + commit() def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): - print(qubit, awg, dig) - quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -524,13 +541,18 @@ def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=Non gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=channelLib.channelDatabase) gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan + commit() -def set_master(awg, trig_channel=2, pulse_length=1e-7): +def set_master(awg, trig_channel, pulse_length=1e-7): + if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): + raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=channelLib.channelDatabase) - st.phys_chan = getattr(awg, f"m{trig_channel}") + st.phys_chan = trig_channel st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} awg.master = True awg.trigger_source = "Internal" + commit() def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' diff --git a/QGL/Channels.py b/QGL/Channels.py index 64fe2910..f1ec2d8a 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -54,6 +54,10 @@ class ChannelDatabase(db.Entity): awgs = Set("AWG", cascade_delete=True) digitizers = Set("Digitizer", cascade_delete=True) time = Optional(datetime.datetime) + def __repr__(self): + return str(self) + def __str__(self): + return "{0}('{1}')".format(self.__class__.__name__, self.label) class MicrowaveSource(db.Entity): label = Required(str) @@ -62,6 +66,10 @@ class MicrowaveSource(db.Entity): power = Optional(float) logical_channel = Optional("PhysicalChannel") channel_db = Optional("ChannelDatabase") + def __repr__(self): + return str(self) + def __str__(self): + return "{0}('{1}')".format(self.__class__.__name__, self.label) class Digitizer(db.Entity): label = Required(str) @@ -76,6 +84,12 @@ class Digitizer(db.Entity): def get_chan(self, name): return self.channels.select(lambda x: x.label.endswith(name)).first() + def ch(self, name): + return self.get_chan(name) + def __repr__(self): + return str(self) + def __str__(self): + return "{0}('{1}')".format(self.__class__.__name__, self.label) class AWG(db.Entity): label = Required(str) @@ -89,6 +103,12 @@ class AWG(db.Entity): def get_chan(self, name): return self.channels.select(lambda x: x.label.endswith(name)).first() + def ch(self, name): + return self.get_chan(name) + def __repr__(self): + return str(self) + def __str__(self): + return "{0}('{1}')".format(self.__class__.__name__, self.label) class Channel(db.Entity): ''' From 0d370f1b87007c4e39695cc5dc40713df9d73a72 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Sun, 24 Jun 2018 21:11:02 -0400 Subject: [PATCH 051/235] Add quick qubit selection function to physical channels --- QGL/Channels.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QGL/Channels.py b/QGL/Channels.py index f1ec2d8a..669b5740 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -139,6 +139,10 @@ class PhysicalChannel(Channel): # marker_channel = Optional("PhysicalMarkerChannel") awg = Optional(AWG) + def q(self): + if isinstance(self.logical_channel, Qubit): + return self.logical_channel + class LogicalChannel(Channel): ''' The main class from which we will generate sequences. From e65a5d314a2ff0dbbceb0499dcad86ccba037c2f Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 25 Jun 2018 13:24:50 -0400 Subject: [PATCH 052/235] Explicitly use "working" channel db rather than hiding "__temp__" --- QGL/ChannelLibraries.py | 27 +++++++++++++++++---------- QGL/Compiler.py | 13 ++++++++++--- 2 files changed, 27 insertions(+), 13 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 55e9be21..375d1496 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -105,6 +105,7 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): channelLib.db.disconnect() config.load_db() + self.database_provider = "sqlite" if database_file: self.database_file = database_file elif config.db_file: @@ -114,7 +115,7 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): self.db = Database() Channels.define_entities(self.db) - self.db.bind('sqlite', filename=self.database_file, create_db=True) + self.db.bind(self.database_provider, filename=self.database_file, create_db=True) self.db.generate_mapping(create_tables=True) # Dirty trick: push the correct entity defs to the calling context @@ -127,10 +128,16 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): self.channelDict = {} # Check to see whether there is already a temp database - for cdb in select(d for d in Channels.ChannelDatabase if d.label == "__temp__"): - self.clear(channel_db=cdb, create_new=False) - - self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + w_dbs = list(select(d for d in Channels.ChannelDatabase if d.label == "working")) + if len(w_dbs) > 1: + # self.clear(channel_db=cdb, create_new=False) + raise Exception("More than one working database exists!") + elif len(w_dbs) == 1: + self.channelDatabase = w_dbs[0] + commit() + self.update_channelDict() + elif len(w_dbs) == 0: + self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) commit() config.load_config() @@ -145,12 +152,12 @@ def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} def ls(self): - select((c.label, c.time, c.id) for c in Channels.ChannelDatabase if c.label != "__temp__").sort_by(1, 2).show() + select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() def ent_by_type_name(self, name, show=False): - q = select(c for c in getattr(Channels,name) if c.label != "__temp__") + q = select(c for c in getattr(Channels,name) if c.label == "working") if show: - select(c.label for c in getattr(Channels,name) if c.label != "__temp__").sort_by(1).show() + select(c.label for c in getattr(Channels,name) if c.label == "working").sort_by(1).show() else: return {el.label: el for el in q} @@ -196,10 +203,10 @@ def clear(self, channel_db=None, create_new=True): commit() # Now clear items that do potentially have sets of items (which should be deleted) for ent in [Channels.ChannelDatabase]: - select(d for d in ent if d.label == "__temp__").delete(bulk=True) + select(d for d in ent if d.label == "working").delete(bulk=True) commit() if create_new: - self.channelDatabase = Channels.ChannelDatabase(label="__temp__", time=datetime.datetime.now()) + self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) commit() def load_obj(self, obj): diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 717e9a4d..9a22d6fb 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -40,9 +40,9 @@ logger = logging.getLogger(__name__) def map_logical_to_physical(wires): - # construct a mapping of physical channels to lists of logical channels - # (there will be more than one logical channel if multiple logical - # channels share a physical channel) + """construct a mapping of physical channels to lists of logical channels + (there will be more than one logical channel if multiple logical + channels share a physical channel)""" physicalChannels = {} for logicalChan in wires.keys(): phys_chan = logicalChan.phys_chan @@ -474,6 +474,12 @@ def compile_to_hardware(seqs, else: extra_meta = awg_metas # create meta output + db_info = { + 'db_provider': ChannelLibraries.channelLib.database_provider, + 'db_filename': ChannelLibraries.channelLib.database_file, + 'library_name': 'working', + 'library_id': ChannelLibraries.channelLib.channelDatabase.id + } if not axis_descriptor: axis_descriptor = [{ 'name': 'segment', @@ -486,6 +492,7 @@ def compile_to_hardware(seqs, if wire.receiver_chan and n>0: receiver_measurements[wire.receiver_chan.label] = n meta = { + 'database_info': db_info, 'instruments': files, 'num_sequences': len(seqs), 'num_measurements': num_measurements, From d5afc711657e30766284bf61dd9e3f6b834a17a8 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 25 Jun 2018 21:39:10 -0400 Subject: [PATCH 053/235] Added bbndb submodule --- .gitmodules | 3 +++ QGL/bbndb | 1 + 2 files changed, 4 insertions(+) create mode 160000 QGL/bbndb diff --git a/.gitmodules b/.gitmodules index e69de29b..d0aadb96 100644 --- a/.gitmodules +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "QGL/bbndb"] + path = QGL/bbndb + url = git@github.com:bbn-q/bbndb diff --git a/QGL/bbndb b/QGL/bbndb new file mode 160000 index 00000000..cd508d77 --- /dev/null +++ b/QGL/bbndb @@ -0,0 +1 @@ +Subproject commit cd508d775ae4154a5bdae88af99a6d72760c63c5 From 1dee44cb5bc1b3820d8426a54d5eac7474677ab2 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 26 Jun 2018 09:28:34 -0400 Subject: [PATCH 054/235] Add requirements file and update setup.py --- .gitmodules | 3 --- QGL/bbndb | 1 - requirements.txt | 7 +++++++ setup.py | 21 ++++++++------------- 4 files changed, 15 insertions(+), 17 deletions(-) delete mode 100644 .gitmodules delete mode 160000 QGL/bbndb create mode 100644 requirements.txt diff --git a/.gitmodules b/.gitmodules deleted file mode 100644 index d0aadb96..00000000 --- a/.gitmodules +++ /dev/null @@ -1,3 +0,0 @@ -[submodule "QGL/bbndb"] - path = QGL/bbndb - url = git@github.com:bbn-q/bbndb diff --git a/QGL/bbndb b/QGL/bbndb deleted file mode 160000 index cd508d77..00000000 --- a/QGL/bbndb +++ /dev/null @@ -1 +0,0 @@ -Subproject commit cd508d775ae4154a5bdae88af99a6d72760c63c5 diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 00000000..ee7ec4e7 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,7 @@ +bbndb >= 0.1 +numpy >= 1.11.1 +scipy >= 0.17.1 +networkx >= 1.11 +h5py >= 2.6.0 +bokeh >= 0.12.13 +jupyter >= 1.0.0 \ No newline at end of file diff --git a/setup.py b/setup.py index dee9e96b..c4db92c2 100644 --- a/setup.py +++ b/setup.py @@ -5,16 +5,11 @@ url='https://github.com/BBN-Q/QGL', packages=find_packages(exclude=["tests"]), install_requires=[ - "numpy >= 1.11.1", - "scipy >= 0.17.1", - "jupyter >= 1.0.0", - "atom >= 0.4.1", - "h5py >= 2.6.0", - "networkx >= 2.0", - "future >= 0.16", - "watchdog >= 0.8.3", - "bokeh >= 0.11", - "ruamel_yaml >= 0.11.14" - ], - extras_require={"gst": "pygsti>0.9.4"} -) + "bbndb >= 0.1", + "numpy >= 1.11.1", + "scipy >= 0.17.1", + "networkx >= 1.11", + "h5py >= 2.6.0", + "bokeh >= 0.12.13", + # "pony >= 0.7.4", # This needs to be 0.7.4-dev + ]) From 95a4cc0019e1259125f6b63e2a5c6b21b68548b0 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 13 Jul 2018 12:04:25 -0400 Subject: [PATCH 055/235] Shifting to bbndb for channel entities --- QGL/ChannelLibraries.py | 19 ++- QGL/Channels.py | 294 +++++----------------------------------- QGL/Compiler.py | 6 + requirements.txt | 1 - 4 files changed, 54 insertions(+), 266 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 375d1496..7f23b5af 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -43,6 +43,7 @@ from . import config from . import Channels from . import PulseShapes +import bbndb channelLib = None @@ -117,6 +118,7 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): Channels.define_entities(self.db) self.db.bind(self.database_provider, filename=self.database_file, create_db=True) self.db.generate_mapping(create_tables=True) + bbndb.database = self.db # Dirty trick: push the correct entity defs to the calling context for var in ["Measurement","Qubit","Edge"]: @@ -149,6 +151,7 @@ def get_current_channels(self): return list(self.channelDatabase.channels) + list(self.channelDatabase.sources) def update_channelDict(self): + commit() self.channelDict = {c.label: c for c in self.get_current_channels()} def ls(self): @@ -461,19 +464,21 @@ def new_APS2(label, address): m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - this_awg = Channels.AWG(label=label, address=address, channels=[chan12, m1, m2, m3, m4], channel_db=channelLib.channelDatabase) + this_awg = Channels.AWG(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=channelLib.channelDatabase) this_awg.trigger_source = "External" this_awg.address = address commit() return this_awg -def new_X6(label, address): - chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel_db=channelLib.channelDatabase) - chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel_db=channelLib.channelDatabase) +def new_X6(label, address, dsp_channel=0, record_length=1024): + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=channelLib.channelDatabase) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=channelLib.channelDatabase) - this_dig = Channels.Digitizer(label=label, address=address, channels=[chan1, chan2], channel_db=channelLib.channelDatabase) + this_dig = Channels.Digitizer(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], + record_length=record_length, channel_db=channelLib.channelDatabase) this_dig.trigger_source = "External" + this_dig.stream_types = "raw, demodulated, integrated, averaged" this_dig.address = address commit() @@ -484,8 +489,8 @@ def new_qubit(label): commit() return thing -def new_source(label, source_type, address, power=-30.0): - thing = Channels.MicrowaveSource(label=label, source_type=source_type, address=address, power=power, channel_db=channelLib.channelDatabase) +def new_source(label, model, address, power=-30.0): + thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, channel_db=channelLib.channelDatabase) commit() return thing diff --git a/QGL/Channels.py b/QGL/Channels.py index 669b5740..2a240ee3 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -23,266 +23,44 @@ from . import config from . import PulseShapes -import numpy as np -from math import tan, cos, pi -from pony.orm import * -from copy import deepcopy -import datetime +from bbndb import define_entities as def_ent +from bbndb import qgl # Get these in global scope for module imports -Channel = None -PhysicalChannel = None -LogicalChannel = None +Channel = None +PhysicalChannel = None +LogicalChannel = None PhysicalQuadratureChannel = None -PhysicalMarkerChannel = None -LogicalMarkerChannel = None -ReceiverChannel = None -Measurement = None -Qubit = None -Edge = None -MicrowaveSource = None -ChannelDatabase = None -Digitizer = None -AWG = None +PhysicalMarkerChannel = None +LogicalMarkerChannel = None +ReceiverChannel = None +Measurement = None +Qubit = None +Edge = None +MicrowaveSource = None +ChannelDatabase = None +Digitizer = None +AWG = None + +# The main definitions have been moved to bbndb +# to keep the schema consistent across Auspex and QGL +# We retain this local framework in order to maintain +# the paths for QGL primitives def define_entities(db): - - class ChannelDatabase(db.Entity): - label = Required(str) - channels = Set("Channel", cascade_delete=True) - sources = Set("MicrowaveSource", cascade_delete=True) - awgs = Set("AWG", cascade_delete=True) - digitizers = Set("Digitizer", cascade_delete=True) - time = Optional(datetime.datetime) - def __repr__(self): - return str(self) - def __str__(self): - return "{0}('{1}')".format(self.__class__.__name__, self.label) - - class MicrowaveSource(db.Entity): - label = Required(str) - source_type = Required(str) - address = Optional(str) - power = Optional(float) - logical_channel = Optional("PhysicalChannel") - channel_db = Optional("ChannelDatabase") - def __repr__(self): - return str(self) - def __str__(self): - return "{0}('{1}')".format(self.__class__.__name__, self.label) - - class Digitizer(db.Entity): - label = Required(str) - address = Optional(str) - # stream_types = Set(str) - channels = Set("ReceiverChannel") - trigger_source = Required(str, default="External", py_check=lambda x: x in ['External', 'Internal']) - channel_db = Optional("ChannelDatabase") - # nbr_segments = Required(int, default=1) # This should be automatic - # nbr_round_robins = Required(int, default=100) # This should be automatic - # acquire_mode = Required(str,default="digitizer", py_check=lambda x: x in ['digitizer', 'averager']) - - def get_chan(self, name): - return self.channels.select(lambda x: x.label.endswith(name)).first() - def ch(self, name): - return self.get_chan(name) - def __repr__(self): - return str(self) - def __str__(self): - return "{0}('{1}')".format(self.__class__.__name__, self.label) - - class AWG(db.Entity): - label = Required(str) - address = Optional(str) - channels = Set("PhysicalChannel", reverse="awg") - trigger_interval = Required(float, default=100e-6) - trigger_source = Required(str, default="External", py_check=lambda x: x in ['External', 'Internal']) - delay = Required(float, default=0.0) - master = Required(bool, default=False) - channel_db = Optional("ChannelDatabase") - - def get_chan(self, name): - return self.channels.select(lambda x: x.label.endswith(name)).first() - def ch(self, name): - return self.get_chan(name) - def __repr__(self): - return str(self) - def __str__(self): - return "{0}('{1}')".format(self.__class__.__name__, self.label) - - class Channel(db.Entity): - ''' - Every channel has a label and some printers. - ''' - label = Required(str) - channel_db = Optional("ChannelDatabase") - - def __repr__(self): - return str(self) - def __str__(self): - return "{0}('{1}')".format(self.__class__.__name__, self.label) - - class PhysicalChannel(Channel): - ''' - The main class for actual AWG channels. - ''' - instrument = Optional(str) # i.e. the AWG or receiver - translator = Optional(str) - generator = Optional(MicrowaveSource, reverse="logical_channel") - sampling_rate = Required(float, default=1.2e9) - delay = Required(float, default=0.0) - - # Required reverse connections - logical_channel = Optional("LogicalChannel") - # quad_channel_I = Optional("PhysicalQuadratureChannel", reverse="I_channel") - # quad_channel_Q = Optional("PhysicalQuadratureChannel", reverse="Q_channel") - # marker_channel = Optional("PhysicalMarkerChannel") - awg = Optional(AWG) - - def q(self): - if isinstance(self.logical_channel, Qubit): - return self.logical_channel - - class LogicalChannel(Channel): - ''' - The main class from which we will generate sequences. - At some point it needs to be assigned to a physical channel. - frequency: modulation frequency of the channel (can be positive or negative) - ''' - #During initilization we may just have a string reference to the channel - phys_chan = Optional(PhysicalChannel) - frequency = Required(float, default=0.0) - pulse_params = Optional(Json, default={}) - gate_chan = Optional("LogicalMarkerChannel") - receiver_chan = Optional("ReceiverChannel") - - class PhysicalMarkerChannel(PhysicalChannel): - ''' - A digital output channel on an AWG. - gate_buffer: How much extra time should be added onto the beginning of a gating pulse - gate_min_width: The minimum marker pulse width - ''' - gate_buffer = Required(float, default=0.0) - gate_min_width = Required(float, default=0.0) - # phys_channel = Optional(PhysicalChannel) - - class PhysicalQuadratureChannel(PhysicalChannel): - ''' - Something used to implement a standard qubit channel with two analog channels and a microwave gating channel. - ''' - amp_factor = Required(float, default=1.0) - phase_skew = Required(float, default=0.0) - I_channel_offset = Required(float, default=0.0) - Q_channel_offset = Required(float, default=0.0) - I_channel_amp_factor = Required(float, default=1.0) - Q_channel_amp_factor = Required(float, default=1.0) - - class ReceiverChannel(PhysicalChannel): - ''' - A trigger input on a receiver. - ''' - triggering_channel = Optional(LogicalChannel) - channel = Optional(int) - stream_type = Required(str, default="Raw", py_check=lambda x: x.lower() in["raw", "demodulated", "integrated", "averaged"]) - if_freq = Required(float, default=0.0) - kernel = Optional(str) - kernel_bias = Required(float, default=0.0) - threshold = Required(float, default=0.0) - threshold_invert = Required(bool, default=False) - digitizer = Optional(Digitizer) - - def pulse_check(name): - return name in ["constant", "gaussian", "drag", "gaussOn", "gaussOff", "dragGaussOn", "dragGaussOff", - "tanh", "exp_decay", "autodyne", "arb_axis_drag"] - - class LogicalMarkerChannel(LogicalChannel): - ''' - A class for digital channels for gating sources or triggering other things. - ''' - meas_channel = Optional(LogicalChannel) - trig_channel = Optional("Measurement") - - def __init__(self, **kwargs): - if "pulse_params" not in kwargs.keys(): - kwargs["pulse_params"] = {'shape_fun': "constant",'length': 10e-9} - super(LogicalMarkerChannel, self).__init__(**kwargs) - - class Qubit(LogicalChannel): - ''' - The main class for generating qubit pulses. Effectively a logical "QuadratureChannel". - frequency: modulation frequency of the channel (can be positive or negative) - ''' - edge_source = Optional("Edge", reverse="source") - edge_target = Optional("Edge", reverse="target") - - def __init__(self, **kwargs): - if "pulse_params" not in kwargs.keys(): - kwargs["pulse_params"] = {'length': 20e-9, - 'piAmp': 1.0, - 'pi2Amp': 0.5, - 'shape_fun': "gaussian", - 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9} - super(Qubit, self).__init__(**kwargs) - - class Measurement(LogicalChannel): - ''' - A class for measurement channels. - Measurements are special because they can be different types: - autodyne which needs an IQ pair or hetero/homodyne which needs just a marker channel. - meas_type: Type of measurement (autodyne, homodyne) - autodyne_freq: use to bake the modulation into the pulse, so that it has constant phase - frequency: use to asssociate modulation with the channel - ''' - meas_type = Required(str, default='autodyne', py_check=lambda x: x in ['autodyne', 'homodyne']) - autodyne_freq = Required(float, default=0.0) - trig_chan = Optional(LogicalMarkerChannel) - - def __init__(self, **kwargs): - if "pulse_params" not in kwargs.keys(): - kwargs["pulse_params"] = {'length': 100e-9, - 'amp': 1.0, - 'shape_fun': "tanh", - 'cutoff': 2, - 'sigma': 1e-9} - super(Measurement, self).__init__(**kwargs) - - class Edge(LogicalChannel): - ''' - Defines an arc/directed edge between qubit vertices. If a device supports bi-directional - connectivity, that is represented with two independent Edges. - - An Edge is also effectively an abstract channel, so it carries the same properties as a - Qubit channel. - ''' - # allow string in source and target so that we can store a label or an object - source = Required(Qubit) - target = Required(Qubit) - - def __init__(self, **kwargs): - if "pulse_params" not in kwargs.keys(): - kwargs["pulse_params"] = {'length': 20e-9, - 'amp': 1.0, - 'phase': 0.0, - 'shape_fun': "gaussian", - 'cutoff': 2, - 'drag_scaling': 0, - 'sigma': 5e-9, - 'riseFall': 20e-9} - super(Edge, self).__init__(**kwargs) - - globals()["Channel"] = Channel - globals()["PhysicalChannel"] = PhysicalChannel - globals()["LogicalChannel"] = LogicalChannel - globals()["PhysicalQuadratureChannel"] = PhysicalQuadratureChannel - globals()["PhysicalMarkerChannel"] = PhysicalMarkerChannel - globals()["LogicalMarkerChannel"] = LogicalMarkerChannel - globals()["ReceiverChannel"] = ReceiverChannel - globals()["Measurement"] = Measurement - globals()["Qubit"] = Qubit - globals()["Edge"] = Edge - globals()["MicrowaveSource"] = MicrowaveSource - globals()["ChannelDatabase"] = ChannelDatabase - globals()["Digitizer"] = Digitizer - globals()["AWG"] = AWG \ No newline at end of file + def_ent(db) + + globals()["Channel"] = qgl.Channel + globals()["PhysicalChannel"] = qgl.PhysicalChannel + globals()["LogicalChannel"] = qgl.LogicalChannel + globals()["PhysicalQuadratureChannel"] = qgl.PhysicalQuadratureChannel + globals()["PhysicalMarkerChannel"] = qgl.PhysicalMarkerChannel + globals()["LogicalMarkerChannel"] = qgl.LogicalMarkerChannel + globals()["ReceiverChannel"] = qgl.ReceiverChannel + globals()["Measurement"] = qgl.Measurement + globals()["Qubit"] = qgl.Qubit + globals()["Edge"] = qgl.Edge + globals()["MicrowaveSource"] = qgl.MicrowaveSource + globals()["ChannelDatabase"] = qgl.ChannelDatabase + globals()["Digitizer"] = qgl.Digitizer + globals()["AWG"] = qgl.AWG \ No newline at end of file diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 9a22d6fb..22efa16c 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -301,6 +301,7 @@ def collect_specializations(seqs): def compile_to_hardware(seqs, fileName, + library_version=None, suffix='', axis_descriptor=None, add_slave_trigger=True, @@ -309,6 +310,9 @@ def compile_to_hardware(seqs, ''' Compiles 'seqs' to a hardware description and saves it to 'fileName'. Other inputs: + library_version (optional): string or ChannelLibrary instance to pack in the + metafile. This will be the version of the library loaded during program + execution. Default None uses the current working version. suffix (optional): string to append to end of fileName, e.g. with fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 axis_descriptor (optional): a list of dictionaries describing the effective @@ -497,6 +501,8 @@ def compile_to_hardware(seqs, 'num_sequences': len(seqs), 'num_measurements': num_measurements, 'axis_descriptor': axis_descriptor, + 'qubits': [c.label for c in channels if isinstance(c, Channels.Qubit)], + 'measurements': [c.label for c in channels if isinstance(c, Channels.Measurement)], 'receivers': receiver_measurements } if extra_meta: diff --git a/requirements.txt b/requirements.txt index ee7ec4e7..e13f6dc7 100644 --- a/requirements.txt +++ b/requirements.txt @@ -4,4 +4,3 @@ scipy >= 0.17.1 networkx >= 1.11 h5py >= 2.6.0 bokeh >= 0.12.13 -jupyter >= 1.0.0 \ No newline at end of file From a0b213d8542695178c2bf234f204389f9643d772 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 18 Jul 2018 09:35:37 -0400 Subject: [PATCH 056/235] Added new_Alazar method --- QGL/ChannelLibraries.py | 17 +++++++++++++++-- QGL/__init__.py | 3 ++- 2 files changed, 17 insertions(+), 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 7f23b5af..a3cc7415 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -158,9 +158,9 @@ def ls(self): select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() def ent_by_type_name(self, name, show=False): - q = select(c for c in getattr(Channels,name) if c.label == "working") + q = select(c for c in getattr(bbndb.qgl,name) if c.channel_db.label == "working") if show: - select(c.label for c in getattr(Channels,name) if c.label == "working").sort_by(1).show() + select(c.label for c in getattr(bbndb.qgl,name) if c.channel_db.label == "working").sort_by(1).show() else: return {el.label: el for el in q} @@ -484,6 +484,19 @@ def new_X6(label, address, dsp_channel=0, record_length=1024): commit() return this_dig +def new_Alazar(label, address, record_length=1024): + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=channelLib.channelDatabase) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=channelLib.channelDatabase) + + this_dig = Channels.Digitizer(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], + record_length=record_length, channel_db=channelLib.channelDatabase) + this_dig.trigger_source = "External" + this_dig.stream_types = "raw" + this_dig.address = address + + commit() + return this_dig + def new_qubit(label): thing = Channels.Qubit(label=label, channel_db=channelLib.channelDatabase) commit() diff --git a/QGL/__init__.py b/QGL/__init__.py index eca7769b..99081cde 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -1,5 +1,6 @@ from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase, AWG, Digitizer -from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib, new_APS2, new_X6, new_qubit, set_control, set_measure, set_master, new_source +from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib +from .ChannelLibraries import new_APS2, new_X6, new_Alazar, new_qubit, set_control, set_measure, set_master, new_source from .PulsePrimitives import * from .Compiler import compile_to_hardware, set_log_level from .PulseSequencer import align From 6797bb4b93c24f2ff78ee3d6115d543b7cebe0ee Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 18 Jul 2018 11:17:43 -0400 Subject: [PATCH 057/235] Setting any channel attribute now invalidates the memoize cache --- QGL/ChannelLibraries.py | 32 ++++++++++++++++---------------- QGL/Channels.py | 6 +++--- QGL/Compiler.py | 5 +++-- QGL/PulsePrimitives.py | 4 ++++ 4 files changed, 26 insertions(+), 21 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index a3cc7415..b7b81cb6 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -43,6 +43,7 @@ from . import config from . import Channels from . import PulseShapes +from .PulsePrimitives import clear_pulse_cache import bbndb channelLib = None @@ -91,7 +92,7 @@ def copy_entity(obj, new_channel_db): obj_attr = getattr(obj, attr.name) if hasattr(obj_attr, "id"): kwargs.pop(attr.name) - links[attr.name] = obj_attr.label + links[attr.name] = obj_attr.label kwargs["channel_db"] = new_channel_db return obj.__class__(**kwargs), links @@ -115,7 +116,7 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): self.database_file = ":memory:" self.db = Database() - Channels.define_entities(self.db) + Channels.define_entities(self.db, cache_callback=clear_pulse_cache) self.db.bind(self.database_provider, filename=self.database_file, create_db=True) self.db.generate_mapping(create_tables=True) bbndb.database = self.db @@ -125,7 +126,7 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): inspect.stack()[1][0].f_globals[var] = getattr(Channels, var) self.connectivityG = nx.DiGraph() - + # This is still somewhere legacy QGL behavior. Massage db into dict for lookup. self.channelDict = {} @@ -203,7 +204,7 @@ def clear(self, channel_db=None, create_new=True): # First clear items that don't have Sets of other items for ent in [Channels.MicrowaveSource, Channels.Channel, Channels.AWG, Channels.Digitizer]: select(c for c in ent if c.channel_db == channel_db).delete(bulk=True) - commit() + commit() # Now clear items that do potentially have sets of items (which should be deleted) for ent in [Channels.ChannelDatabase]: select(d for d in ent if d.label == "working").delete(bulk=True) @@ -228,7 +229,7 @@ def save_as(self, name): new_chans, new_srcs, new_awgs, new_digs = copy_objs(chans, srcs, awgs, digs, new_channel_db=cd) cd.channels, cd.sources, cd.awgs, cd.digitizers = new_chans, new_srcs, new_awgs, new_digs commit() - + #Dictionary methods def __getitem__(self, key): return self.channelDict[key] @@ -463,7 +464,7 @@ def new_APS2(label, address): m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - + this_awg = Channels.AWG(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=channelLib.channelDatabase) this_awg.trigger_source = "External" this_awg.address = address @@ -474,7 +475,7 @@ def new_APS2(label, address): def new_X6(label, address, dsp_channel=0, record_length=1024): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=channelLib.channelDatabase) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=channelLib.channelDatabase) - + this_dig = Channels.Digitizer(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], record_length=record_length, channel_db=channelLib.channelDatabase) this_dig.trigger_source = "External" @@ -524,7 +525,7 @@ def set_control(qubit, awg, generator=None): if generator: qubit.phys_chan.generator = generator commit() - + def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -542,14 +543,14 @@ def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=Non meas.phys_chan = phys_chan if generator: meas.phys_chan.generator = generator - + phys_trig_channel = trig_channel if trig_channel else awg.get_chan("12m1") trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=channelLib.channelDatabase) trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan - + if isinstance(dig, Channels.Digitizer) and len(dig.channels) > 1: raise ValueError("In set_measure the Digitizer must have a single receiver channel or a specific channel must be passed instead") elif isinstance(dig, Channels.Digitizer) and len(dig.channels) == 1: @@ -567,11 +568,11 @@ def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=Non gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan commit() - + def set_master(awg, trig_channel, pulse_length=1e-7): if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") - + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=channelLib.channelDatabase) st.phys_chan = trig_channel st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} @@ -583,15 +584,15 @@ def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' # TODO: this will just get the first entry in the whole damned DB! # thing = select(el for el in Channels.Qubit if el.label==label).first() - thing = {c.label: c for c in channelLib.get_current_channels()}[label] + thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Qubit)}[label] if thing: return thing else: return Channels.Qubit(label=label, **kwargs) - + def MeasFactory(label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' - thing = {c.label: c for c in channelLib.get_current_channels()}[label] + thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Measurement)}[label] if thing: return thing else: @@ -613,4 +614,3 @@ def EdgeFactory(source, target): else: raise ValueError('Edge {0} not found in connectivity graph'.format(( source, target))) - diff --git a/QGL/Channels.py b/QGL/Channels.py index 2a240ee3..abb1a6c2 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -47,8 +47,8 @@ # We retain this local framework in order to maintain # the paths for QGL primitives -def define_entities(db): - def_ent(db) +def define_entities(db, cache_callback=None): + def_ent(db, cache_callback=cache_callback) globals()["Channel"] = qgl.Channel globals()["PhysicalChannel"] = qgl.PhysicalChannel @@ -63,4 +63,4 @@ def define_entities(db): globals()["MicrowaveSource"] = qgl.MicrowaveSource globals()["ChannelDatabase"] = qgl.ChannelDatabase globals()["Digitizer"] = qgl.Digitizer - globals()["AWG"] = qgl.AWG \ No newline at end of file + globals()["AWG"] = qgl.AWG diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 22efa16c..3ee00996 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -30,7 +30,7 @@ from . import Channels from . import ChannelLibraries from . import PulseShapes -from .PulsePrimitives import Id +from .PulsePrimitives import Id, clear_pulse_cache from .PulseSequencer import Pulse, PulseBlock, CompositePulse from . import ControlFlow from . import BlockLabel @@ -323,6 +323,7 @@ def compile_to_hardware(seqs, tdm_seq (optional): compile for TDM ''' ChannelLibraries.channelLib.update_channelDict() + # clear_pulse_cache() logger.debug("Compiling %d sequence(s)", len(seqs)) @@ -502,7 +503,7 @@ def compile_to_hardware(seqs, 'num_measurements': num_measurements, 'axis_descriptor': axis_descriptor, 'qubits': [c.label for c in channels if isinstance(c, Channels.Qubit)], - 'measurements': [c.label for c in channels if isinstance(c, Channels.Measurement)], + 'measurements': [c.label for c in channels if isinstance(c, Channels.Measurement)], 'receivers': receiver_measurements } if extra_meta: diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 59d48099..538f6714 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -46,6 +46,9 @@ def cacheWrap(*args, **kwargs): key = (pulseFunc, args) if key not in _memoize.cache: _memoize.cache[key] = pulseFunc(*args) + print("Caching", pulseFunc) + else: + print("Recalling", pulseFunc) return _memoize.cache[key] return cacheWrap @@ -757,6 +760,7 @@ def _MEAS(qubit, **kwargs): params['frequency'] = measChan.autodyne_freq params['baseShape'] = params.pop('shape_fun') params['shape_fun'] = PulseShapes.autodyne + print(params) amp = params.pop('amp') ignoredStrParams = ['phase', 'frameChange'] if 'amp' not in kwargs: From 492de567efe5d74be0e1619b972792522264a9da Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 18 Jul 2018 17:35:55 -0400 Subject: [PATCH 058/235] Remove overly verbose code from aed6222 --- QGL/PulsePrimitives.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 538f6714..59d48099 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -46,9 +46,6 @@ def cacheWrap(*args, **kwargs): key = (pulseFunc, args) if key not in _memoize.cache: _memoize.cache[key] = pulseFunc(*args) - print("Caching", pulseFunc) - else: - print("Recalling", pulseFunc) return _memoize.cache[key] return cacheWrap @@ -760,7 +757,6 @@ def _MEAS(qubit, **kwargs): params['frequency'] = measChan.autodyne_freq params['baseShape'] = params.pop('shape_fun') params['shape_fun'] = PulseShapes.autodyne - print(params) amp = params.pop('amp') ignoredStrParams = ['phase', 'frameChange'] if 'amp' not in kwargs: From dfef1cfe6e57dce828d0c54f57dbece2e9b5edda Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 19 Jul 2018 21:39:51 -0400 Subject: [PATCH 059/235] Created wrapper to convert func args that are entities into unique IDs to avoid crossing db_sessions --- QGL/ChannelLibraries.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index b7b81cb6..1d2e2a23 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -37,6 +37,7 @@ import datetime import importlib import inspect +from functools import wraps from pony.orm import * import networkx as nx @@ -47,6 +48,21 @@ import bbndb channelLib = None +db = None + +def localize_db_objects(f): + """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" + @wraps(f) + def wrapper(*args, **kwargs): + global db + with db_session: + args_info = [(type(arg), arg.id) if isinstance(arg, db.Entity) else (arg, None) for arg in args] + kwargs_info = {k: (type(v), v.id) if isinstance(v, db.Entity) else (v, None) for k, v in kwargs.items()} + with db_session: + new_args = [c[i] if i else c for c, i in args_info] + new_kwargs = {k: v[0][v[1]] if v[1] else v[0] for k,v in kwargs_info.items()} + return f(*new_args, **new_kwargs) + return wrapper def set_from_dict(obj, settings): for prop_name in obj.to_dict().keys(): From ccc4a1dda0e3e0fc241fd970caf0d302eea50acd Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 19 Jul 2018 21:40:24 -0400 Subject: [PATCH 060/235] Convert channel library to properly using db_sessions for non-interactive pony use --- QGL/ChannelLibraries.py | 112 +++++++++++++++++++++++++--------------- 1 file changed, 70 insertions(+), 42 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 1d2e2a23..d16b7c93 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -72,6 +72,7 @@ def set_from_dict(obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") +@db_session def copy_objs(*entities, new_channel_db): # Entities is a list of lists of entities of specific types new_entities = [] @@ -97,6 +98,7 @@ def copy_objs(*entities, new_channel_db): return new_entities +@db_session def copy_entity(obj, new_channel_db): """Copy a pony entity instance""" kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_ if a.name not in ["id", "classtype"]} @@ -118,7 +120,7 @@ class ChannelLibrary(object): def __init__(self, database_file=None, channelDict={}, **kwargs): """Create the channel library.""" - global channelLib + global channelLib, db if channelLib is not None: channelLib.db.disconnect() @@ -131,11 +133,11 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): else: self.database_file = ":memory:" - self.db = Database() - Channels.define_entities(self.db, cache_callback=clear_pulse_cache) - self.db.bind(self.database_provider, filename=self.database_file, create_db=True) - self.db.generate_mapping(create_tables=True) - bbndb.database = self.db + db = Database() + Channels.define_entities(db, cache_callback=clear_pulse_cache) + db.bind(self.database_provider, filename=self.database_file, create_db=True) + db.generate_mapping(create_tables=True) + bbndb.database = db # Dirty trick: push the correct entity defs to the calling context for var in ["Measurement","Qubit","Edge"]: @@ -147,33 +149,39 @@ def __init__(self, database_file=None, channelDict={}, **kwargs): self.channelDict = {} # Check to see whether there is already a temp database - w_dbs = list(select(d for d in Channels.ChannelDatabase if d.label == "working")) - if len(w_dbs) > 1: - # self.clear(channel_db=cdb, create_new=False) - raise Exception("More than one working database exists!") - elif len(w_dbs) == 1: - self.channelDatabase = w_dbs[0] - commit() - self.update_channelDict() - elif len(w_dbs) == 0: - self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) - commit() + with db_session: + w_dbs = list(select(d for d in Channels.ChannelDatabase if d.label == "working")) + if len(w_dbs) > 1: + # self.clear(channel_db=cdb, create_new=False) + raise Exception("More than one working database exists!") + elif len(w_dbs) == 1: + self.channelDatabase = w_dbs[0] + commit() + self.update_channelDict() + elif len(w_dbs) == 0: + self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) + # commit() config.load_config() # Update the global reference channelLib = self + @db_session def get_current_channels(self): - return list(self.channelDatabase.channels) + list(self.channelDatabase.sources) + cdb = Channels.ChannelDatabase[self.channelDatabase.id] # Can't use external object + return list(cdb.channels) + list(cdb.sources) + @db_session def update_channelDict(self): commit() self.channelDict = {c.label: c for c in self.get_current_channels()} + @db_session def ls(self): select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() + @db_session def ent_by_type_name(self, name, show=False): q = select(c for c in getattr(bbndb.qgl,name) if c.channel_db.label == "working") if show: @@ -205,15 +213,18 @@ def ls_qubit(self): def ls_meas(self): return self.ent_by_type_name("Measurement", show=True) + @db_session def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ obj = list(select(c for c in Channels.ChannelDatabase if c.label==name).sort_by(desc(Channels.ChannelDatabase.time))) self.load_obj(obj[-index]) + @db_session def load_by_id(self, id_num): obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() self.load_obj(obj) + @db_session def clear(self, channel_db=None, create_new=True): # If no database is specified, clear self.database channel_db = channel_db if channel_db else self.channelDatabase @@ -229,6 +240,7 @@ def clear(self, channel_db=None, create_new=True): self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) commit() + @db_session def load_obj(self, obj): commit() self.clear() @@ -237,6 +249,7 @@ def load_obj(self, obj): commit() self.update_channelDict() + @db_session def save_as(self, name): chans, srcs, awgs, digs = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, self.channelDatabase.awgs, self.channelDatabase.digitizers]) @@ -265,6 +278,7 @@ def keys(self): def values(self): return self.channelDict.values() + @db_session def build_connectivity_graph(self): # build connectivity graph <<<<<<< HEAD @@ -474,26 +488,30 @@ def load_from_library(self, return_only=False): # Convenience functions for generating and linking channels # TODO: move these to a shim layer shared by Auspex/QGL +@db_session def new_APS2(label, address): - chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=channelLib.channelDatabase) - - this_awg = Channels.AWG(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=channelLib.channelDatabase) + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=cdb) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=cdb) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=cdb) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=cdb) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=cdb) + + this_awg = Channels.AWG(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=cdb) this_awg.trigger_source = "External" this_awg.address = address commit() return this_awg +@db_session def new_X6(label, address, dsp_channel=0, record_length=1024): - chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=channelLib.channelDatabase) - chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=channelLib.channelDatabase) + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=cdb) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=cdb) this_dig = Channels.Digitizer(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], - record_length=record_length, channel_db=channelLib.channelDatabase) + record_length=record_length, channel_db=cdb) this_dig.trigger_source = "External" this_dig.stream_types = "raw, demodulated, integrated, averaged" this_dig.address = address @@ -501,12 +519,14 @@ def new_X6(label, address, dsp_channel=0, record_length=1024): commit() return this_dig +@db_session def new_Alazar(label, address, record_length=1024): - chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=channelLib.channelDatabase) - chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=channelLib.channelDatabase) + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=cdb) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=cdb) this_dig = Channels.Digitizer(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], - record_length=record_length, channel_db=channelLib.channelDatabase) + record_length=record_length, channel_db=cdb) this_dig.trigger_source = "External" this_dig.stream_types = "raw" this_dig.address = address @@ -514,16 +534,19 @@ def new_Alazar(label, address, record_length=1024): commit() return this_dig +@db_session def new_qubit(label): - thing = Channels.Qubit(label=label, channel_db=channelLib.channelDatabase) - commit() + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + thing = Channels.Qubit(label=label, channel_db=cdb) return thing +@db_session def new_source(label, model, address, power=-30.0): - thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, channel_db=channelLib.channelDatabase) - commit() + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, channel_db=cdb) return thing +@localize_db_objects def set_control(qubit, awg, generator=None): quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -540,9 +563,10 @@ def set_control(qubit, awg, generator=None): qubit.phys_chan = phys_chan if generator: qubit.phys_chan.generator = generator - commit() +@localize_db_objects def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -555,14 +579,14 @@ def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=Non else: raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") - meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=channelLib.channelDatabase) + meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=cdb) meas.phys_chan = phys_chan if generator: meas.phys_chan.generator = generator phys_trig_channel = trig_channel if trig_channel else awg.get_chan("12m1") - trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=channelLib.channelDatabase) + trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=cdb) trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan @@ -580,22 +604,23 @@ def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=Non if gate: phys_gate_channel = gate_channel if gate_channel else awg.get_chan("12m2") - gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=channelLib.channelDatabase) + gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=cdb) gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan - commit() +@localize_db_objects def set_master(awg, trig_channel, pulse_length=1e-7): if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") - st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=channelLib.channelDatabase) + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=cdb) st.phys_chan = trig_channel st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} awg.master = True awg.trigger_source = "Internal" - commit() +@db_session def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' # TODO: this will just get the first entry in the whole damned DB! @@ -606,6 +631,7 @@ def QubitFactory(label, **kwargs): else: return Channels.Qubit(label=label, **kwargs) +@db_session def MeasFactory(label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Measurement)}[label] @@ -614,6 +640,7 @@ def MeasFactory(label, **kwargs): else: return Channels.Measurement(label=label, **kwargs) +@db_session def MarkerFactory(label, **kwargs): ''' Return a saved Marker channel or create a new one. ''' thing = {c.label: c for c in channelLib.get_current_channels()}[label] @@ -622,6 +649,7 @@ def MarkerFactory(label, **kwargs): else: return Channels.LogicalMarkerChannel(label=label, **kwargs) +@db_session def EdgeFactory(source, target): if channelLib.connectivityG.has_edge(source, target): return channelLib.connectivityG[source][target]['channel'] From a508cfd678bdfc8f8fb79e66a6424542a9ee4620 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 19 Sep 2018 11:42:39 -0400 Subject: [PATCH 061/235] Frequency now required for sources --- QGL/ChannelLibraries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index d16b7c93..41bbe8c8 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -541,9 +541,9 @@ def new_qubit(label): return thing @db_session -def new_source(label, model, address, power=-30.0): +def new_source(label, model, address, power=-30.0, frequency=5.0e9): cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, channel_db=cdb) + thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=cdb) return thing @localize_db_objects From dd709e2d1e52a67bef201afcb59bbf4d7c2b63b7 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 2 Oct 2018 21:49:06 -0400 Subject: [PATCH 062/235] Add transceiver object and rename AWG->transmitter, dig->receiver --- QGL/ChannelLibraries.py | 141 ++++++++++++++++++++++------------------ QGL/Channels.py | 10 +-- QGL/__init__.py | 4 +- 3 files changed, 87 insertions(+), 68 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 41bbe8c8..81f54d12 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -39,6 +39,7 @@ import inspect from functools import wraps from pony.orm import * +import numpy as np import networkx as nx from . import config @@ -189,25 +190,25 @@ def ent_by_type_name(self, name, show=False): else: return {el.label: el for el in q} - def dig(self): - return self.ent_by_type_name("Digitizer") + def receivers(self): + return self.ent_by_type_name("Receiver") - def awg(self): - return self.ent_by_type_name("AWG") + def transmitter(self): + return self.ent_by_type_name("Transmitter") - def qubit(self): + def qubits(self): return self.ent_by_type_name("Qubit") def meas(self): return self.ent_by_type_name("Measurement") - def ls_dig(self): - return self.ent_by_type_name("Digitizer", show=True) + def ls_receivers(self): + return self.ent_by_type_name("Receiver", show=True) - def ls_awg(self): - return self.ent_by_type_name("AWG", show=True) + def ls_transmitters(self): + return self.ent_by_type_name("Transmitter", show=True) - def ls_qubit(self): + def ls_qubits(self): return self.ent_by_type_name("Qubit", show=True) def ls_meas(self): @@ -229,7 +230,7 @@ def clear(self, channel_db=None, create_new=True): # If no database is specified, clear self.database channel_db = channel_db if channel_db else self.channelDatabase # First clear items that don't have Sets of other items - for ent in [Channels.MicrowaveSource, Channels.Channel, Channels.AWG, Channels.Digitizer]: + for ent in [Channels.MicrowaveSource, Channels.Channel, Channels.Transmitter, Channels.Receiver, Channels.Transceiver]: select(c for c in ent if c.channel_db == channel_db).delete(bulk=True) commit() # Now clear items that do potentially have sets of items (which should be deleted) @@ -244,19 +245,20 @@ def clear(self, channel_db=None, create_new=True): def load_obj(self, obj): commit() self.clear() - chans, srcs, awgs, digs = map(list, [obj.channels, obj.sources, obj.awgs, obj.digitizers]) - copy_objs(chans, srcs, awgs, digs, new_channel_db=self.channelDatabase) + chans, srcs, d2as, a2ds, trans = map(list, [obj.channels, obj.sources, obj.transmitters, obj.receivers, obj.transceivers]) + copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=self.channelDatabase) commit() self.update_channelDict() @db_session def save_as(self, name): - chans, srcs, awgs, digs = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, - self.channelDatabase.awgs, self.channelDatabase.digitizers]) + chans, srcs, d2as, a2dsm, trans = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, + self.channelDatabase.transmitters, self.channelDatabase.receivers, + self.channelDatabase.transceivers]) commit() cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now()) - new_chans, new_srcs, new_awgs, new_digs = copy_objs(chans, srcs, awgs, digs, new_channel_db=cd) - cd.channels, cd.sources, cd.awgs, cd.digitizers = new_chans, new_srcs, new_awgs, new_digs + new_chans, new_srcs, new_d2as, new_a2ds, new_trans = copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=cd) + cd.channels, cd.sources, cd.transmitters, cd.receivers, cd.transceivers = new_chans, new_srcs, new_d2as, new_a2ds, new_trans commit() #Dictionary methods @@ -497,12 +499,23 @@ def new_APS2(label, address): m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=cdb) m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=cdb) - this_awg = Channels.AWG(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=cdb) - this_awg.trigger_source = "External" - this_awg.address = address + this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=cdb) + this_transmitter.trigger_source = "External" + this_transmitter.address = address commit() - return this_awg + return this_transmitter + +@db_session +def new_APS2_rack(label, num, start_address): + cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object + address_start = ".".join(start_address.split(".")[:3]) + address_end = int(start_address.split(".")[-1]) + transmitters = [new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] + this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=cdb) + + commit() + return this_transceiver @db_session def new_X6(label, address, dsp_channel=0, record_length=1024): @@ -510,14 +523,18 @@ def new_X6(label, address, dsp_channel=0, record_length=1024): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=cdb) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=cdb) - this_dig = Channels.Digitizer(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], + this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], record_length=record_length, channel_db=cdb) - this_dig.trigger_source = "External" - this_dig.stream_types = "raw, demodulated, integrated, averaged" - this_dig.address = address + this_receiver.trigger_source = "External" + this_receiver.stream_types = "raw, demodulated, integrated" + this_receiver.address = address + + # Add a default kernel + chan1.kernel = np.ones(record_length, dtype=np.complex).tobytes() + chan2.kernel = np.ones(record_length, dtype=np.complex).tobytes() commit() - return this_dig + return this_receiver @db_session def new_Alazar(label, address, record_length=1024): @@ -525,14 +542,14 @@ def new_Alazar(label, address, record_length=1024): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=cdb) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=cdb) - this_dig = Channels.Digitizer(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], + this_receiver = Channels.Receiver(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], record_length=record_length, channel_db=cdb) - this_dig.trigger_source = "External" - this_dig.stream_types = "raw" - this_dig.address = address + this_receiver.trigger_source = "External" + this_receiver.stream_types = "raw" + this_receiver.address = address commit() - return this_dig + return this_receiver @db_session def new_qubit(label): @@ -547,69 +564,69 @@ def new_source(label, model, address, power=-30.0, frequency=5.0e9): return thing @localize_db_objects -def set_control(qubit, awg, generator=None): - quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] - markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] +def set_control(qubit, transmitter, generator=None): + quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] - if isinstance(awg, Channels.AWG) and len(quads) > 1: - raise ValueError("In set_control the AWG must have a single quadrature channel or a specific channel must be passed instead") - elif isinstance(awg, Channels.AWG) and len(quads) == 1: + if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: + raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: phys_chan = quads[0] - elif isinstance(awg, Channels.PhysicalQuadratureChannel): - phys_chan = awg + elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): + phys_chan = transmitter else: - raise ValueError("In set_control the AWG must have a single quadrature channel or a specific channel must be passed instead") + raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") qubit.phys_chan = phys_chan if generator: qubit.phys_chan.generator = generator @localize_db_objects -def set_measure(qubit, awg, dig, generator=None, dig_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): +def set_measure(qubit, transmitter, receivers, generator=None, receivers_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - quads = [c for c in awg.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] - markers = [c for c in awg.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] - if isinstance(awg, Channels.AWG) and len(quads) > 1: - raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") - elif isinstance(awg, Channels.AWG) and len(quads) == 1: + if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: phys_chan = quads[0] - elif isinstance(awg, Channels.PhysicalQuadratureChannel): - phys_chan = awg + elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): + phys_chan = transmitter else: - raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=cdb) meas.phys_chan = phys_chan if generator: meas.phys_chan.generator = generator - phys_trig_channel = trig_channel if trig_channel else awg.get_chan("12m1") + phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("12m1") - trig_chan = Channels.LogicalMarkerChannel(label=f"digTrig-{qubit.label}", channel_db=cdb) + trig_chan = Channels.LogicalMarkerChannel(label=f"receiversTrig-{qubit.label}", channel_db=cdb) trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan - if isinstance(dig, Channels.Digitizer) and len(dig.channels) > 1: - raise ValueError("In set_measure the Digitizer must have a single receiver channel or a specific channel must be passed instead") - elif isinstance(dig, Channels.Digitizer) and len(dig.channels) == 1: - rcv_chan = dig.channels[0] - elif isinstance(dig, Channels.ReceiverChannel): - rcv_chan = dig + if isinstance(receivers, Channels.Receiver) and len(receivers.channels) > 1: + raise ValueError("In set_measure the Receiver must have a single receiver channel or a specific channel must be passed instead") + elif isinstance(receivers, Channels.Receiver) and len(receivers.channels) == 1: + rcv_chan = receivers.channels[0] + elif isinstance(receivers, Channels.ReceiverChannel): + rcv_chan = receivers else: - raise ValueError("In set_measure the AWG must have a single quadrature channel or a specific channel must be passed instead") + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") meas.receiver_chan = rcv_chan if gate: - phys_gate_channel = gate_channel if gate_channel else awg.get_chan("12m2") + phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("12m2") gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=cdb) gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan @localize_db_objects -def set_master(awg, trig_channel, pulse_length=1e-7): +def set_master(transmitter, trig_channel, pulse_length=1e-7): if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") @@ -617,8 +634,8 @@ def set_master(awg, trig_channel, pulse_length=1e-7): st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=cdb) st.phys_chan = trig_channel st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} - awg.master = True - awg.trigger_source = "Internal" + transmitter.master = True + transmitter.trigger_source = "Internal" @db_session def QubitFactory(label, **kwargs): diff --git a/QGL/Channels.py b/QGL/Channels.py index abb1a6c2..2e1a2a8c 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -39,8 +39,9 @@ Edge = None MicrowaveSource = None ChannelDatabase = None -Digitizer = None -AWG = None +Receiver = None +Transmitter = None +Transceiver = None # The main definitions have been moved to bbndb # to keep the schema consistent across Auspex and QGL @@ -62,5 +63,6 @@ def define_entities(db, cache_callback=None): globals()["Edge"] = qgl.Edge globals()["MicrowaveSource"] = qgl.MicrowaveSource globals()["ChannelDatabase"] = qgl.ChannelDatabase - globals()["Digitizer"] = qgl.Digitizer - globals()["AWG"] = qgl.AWG + globals()["Receiver"] = qgl.Receiver + globals()["Transmitter"] = qgl.Transmitter + globals()["Transceiver"] = qgl.Transceiver diff --git a/QGL/__init__.py b/QGL/__init__.py index 99081cde..b82dc058 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -1,6 +1,6 @@ -from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase, AWG, Digitizer +from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase, Transmitter, Receiver, Transceiver from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib -from .ChannelLibraries import new_APS2, new_X6, new_Alazar, new_qubit, set_control, set_measure, set_master, new_source +from .ChannelLibraries import new_APS2, new_X6, new_APS2_rack, new_Alazar, new_qubit, set_control, set_measure, set_master, new_source from .PulsePrimitives import * from .Compiler import compile_to_hardware, set_log_level from .PulseSequencer import align From 99d30b87a39c4d8eb1b13c6a5092b9c6708e2a37 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 9 Oct 2018 17:21:54 -0400 Subject: [PATCH 063/235] Preliminary functionality moving to sqlalchemy from pony (sigh of relief) --- QGL/ChannelLibraries.py | 436 ++++++++++++++++++---------------------- QGL/Channels.py | 44 +--- QGL/Compiler.py | 4 +- QGL/__init__.py | 4 +- QGL/config.py | 11 +- 5 files changed, 208 insertions(+), 291 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 81f54d12..1f62d3ac 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -38,34 +38,33 @@ import importlib import inspect from functools import wraps -from pony.orm import * import numpy as np import networkx as nx + +import bbndb from . import config from . import Channels from . import PulseShapes from .PulsePrimitives import clear_pulse_cache -import bbndb channelLib = None -db = None - -def localize_db_objects(f): - """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" - @wraps(f) - def wrapper(*args, **kwargs): - global db - with db_session: - args_info = [(type(arg), arg.id) if isinstance(arg, db.Entity) else (arg, None) for arg in args] - kwargs_info = {k: (type(v), v.id) if isinstance(v, db.Entity) else (v, None) for k, v in kwargs.items()} - with db_session: - new_args = [c[i] if i else c for c, i in args_info] - new_kwargs = {k: v[0][v[1]] if v[1] else v[0] for k,v in kwargs_info.items()} - return f(*new_args, **new_kwargs) - return wrapper - -def set_from_dict(obj, settings): + +# def localize_db_objects(f): + # """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" + # @wraps(f) + # def wrapper(*args, **kwargs): + # global db + # with db_session: + # args_info = [(type(arg), arg.id) if isinstance(arg, db.Entity) else (arg, None) for arg in args] + # kwargs_info = {k: (type(v), v.id) if isinstance(v, db.Entity) else (v, None) for k, v in kwargs.items()} + # with db_session: + # new_args = [c[i] if i else c for c, i in args_info] + # new_kwargs = {k: v[0][v[1]] if v[1] else v[0] for k,v in kwargs_info.items()} + # return f(*new_args, **new_kwargs) + # return wrapper + +def set_from_dict(self, obj, settings): for prop_name in obj.to_dict().keys(): if prop_name in settings.keys(): try: @@ -73,7 +72,6 @@ def set_from_dict(obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") -@db_session def copy_objs(*entities, new_channel_db): # Entities is a list of lists of entities of specific types new_entities = [] @@ -99,7 +97,6 @@ def copy_objs(*entities, new_channel_db): return new_entities -@db_session def copy_entity(obj, new_channel_db): """Copy a pony entity instance""" kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_ if a.name not in ["id", "classtype"]} @@ -118,71 +115,57 @@ def copy_entity(obj, new_channel_db): class ChannelLibrary(object): - def __init__(self, database_file=None, channelDict={}, **kwargs): + def __init__(self, db_resource_name=None): """Create the channel library.""" - global channelLib, db - if channelLib is not None: - channelLib.db.disconnect() + global channelLib - config.load_db() - self.database_provider = "sqlite" - if database_file: - self.database_file = database_file - elif config.db_file: - self.database_file = config.db_file - else: - self.database_file = ":memory:" + self.db_provider = "sqlite" + self.db_resource_name = ":memory:" - db = Database() - Channels.define_entities(db, cache_callback=clear_pulse_cache) - db.bind(self.database_provider, filename=self.database_file, create_db=True) - db.generate_mapping(create_tables=True) - bbndb.database = db - - # Dirty trick: push the correct entity defs to the calling context - for var in ["Measurement","Qubit","Edge"]: - inspect.stack()[1][0].f_globals[var] = getattr(Channels, var) + if bbndb.engine: + # Use current db + self.db = bbndb.engine + else: + + if db_resource_name: + self.db_resource_name = db_resource_name + elif config.load_db(): + self.db_resource_name = config.load_db() + + self.db = bbndb.engine = bbndb.create_engine(f'{self.db_provider}:///{self.db_resource_name}', echo=False) + + bbndb.Base.metadata.create_all(bbndb.engine) + bbndb.Session.configure(bind=bbndb.engine) + self.Session = bbndb.Session + self.session = self.Session() self.connectivityG = nx.DiGraph() - # This is still somewhere legacy QGL behavior. Massage db into dict for lookup. - self.channelDict = {} - # Check to see whether there is already a temp database - with db_session: - w_dbs = list(select(d for d in Channels.ChannelDatabase if d.label == "working")) - if len(w_dbs) > 1: - # self.clear(channel_db=cdb, create_new=False) - raise Exception("More than one working database exists!") - elif len(w_dbs) == 1: - self.channelDatabase = w_dbs[0] - commit() - self.update_channelDict() - elif len(w_dbs) == 0: - self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) - # commit() - - config.load_config() + working_dbs = self.session.query(Channels.ChannelDatabase).filter_by(label="working").all() + if len(working_dbs) > 1: + raise Exception("More than one working database exists!") + elif len(working_dbs) == 1: + self.channelDatabase = working_dbs[0] + elif len(working_dbs) == 0: + self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) + self.session.add(self.channelDatabase) + + self.update_channelDict() # Update the global reference channelLib = self - @db_session def get_current_channels(self): - cdb = Channels.ChannelDatabase[self.channelDatabase.id] # Can't use external object - return list(cdb.channels) + list(cdb.sources) + return self.channelDatabase.channels + self.channelDatabase.generators - @db_session def update_channelDict(self): - commit() self.channelDict = {c.label: c for c in self.get_current_channels()} - @db_session def ls(self): select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() - @db_session def ent_by_type_name(self, name, show=False): q = select(c for c in getattr(bbndb.qgl,name) if c.channel_db.label == "working") if show: @@ -214,52 +197,40 @@ def ls_qubits(self): def ls_meas(self): return self.ent_by_type_name("Measurement", show=True) - @db_session def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ obj = list(select(c for c in Channels.ChannelDatabase if c.label==name).sort_by(desc(Channels.ChannelDatabase.time))) self.load_obj(obj[-index]) - @db_session def load_by_id(self, id_num): obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() self.load_obj(obj) - @db_session def clear(self, channel_db=None, create_new=True): # If no database is specified, clear self.database channel_db = channel_db if channel_db else self.channelDatabase # First clear items that don't have Sets of other items for ent in [Channels.MicrowaveSource, Channels.Channel, Channels.Transmitter, Channels.Receiver, Channels.Transceiver]: select(c for c in ent if c.channel_db == channel_db).delete(bulk=True) - commit() # Now clear items that do potentially have sets of items (which should be deleted) for ent in [Channels.ChannelDatabase]: select(d for d in ent if d.label == "working").delete(bulk=True) - commit() if create_new: - self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) - commit() + print("created new database with id", self.channelDatabase.id) + channelLib = self - @db_session def load_obj(self, obj): - commit() self.clear() chans, srcs, d2as, a2ds, trans = map(list, [obj.channels, obj.sources, obj.transmitters, obj.receivers, obj.transceivers]) copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=self.channelDatabase) - commit() self.update_channelDict() - @db_session def save_as(self, name): chans, srcs, d2as, a2dsm, trans = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, self.channelDatabase.transmitters, self.channelDatabase.receivers, self.channelDatabase.transceivers]) - commit() - cd = Channels.ChannelDatabase(label=name, time=datetime.datetime.now()) new_chans, new_srcs, new_d2as, new_a2ds, new_trans = copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=cd) cd.channels, cd.sources, cd.transmitters, cd.receivers, cd.transceivers = new_chans, new_srcs, new_d2as, new_a2ds, new_trans - commit() #Dictionary methods def __getitem__(self, key): @@ -280,7 +251,6 @@ def keys(self): def values(self): return self.channelDict.values() - @db_session def build_connectivity_graph(self): # build connectivity graph <<<<<<< HEAD @@ -487,158 +457,149 @@ def load_from_library(self, return_only=False): self.connectivityG[chan.source][chan.target]['channel'] = chan >>>>>>> Ditch atom, move to Pony.orm for all channel library objects. -# Convenience functions for generating and linking channels -# TODO: move these to a shim layer shared by Auspex/QGL - -@db_session -def new_APS2(label, address): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=cdb) - m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=cdb) - m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=cdb) - m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=cdb) - m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=cdb) - - this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=cdb) - this_transmitter.trigger_source = "External" - this_transmitter.address = address - - commit() - return this_transmitter - -@db_session -def new_APS2_rack(label, num, start_address): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - address_start = ".".join(start_address.split(".")[:3]) - address_end = int(start_address.split(".")[-1]) - transmitters = [new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] - this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=cdb) - - commit() - return this_transceiver - -@db_session -def new_X6(label, address, dsp_channel=0, record_length=1024): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=cdb) - chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=cdb) - - this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], - record_length=record_length, channel_db=cdb) - this_receiver.trigger_source = "External" - this_receiver.stream_types = "raw, demodulated, integrated" - this_receiver.address = address - - # Add a default kernel - chan1.kernel = np.ones(record_length, dtype=np.complex).tobytes() - chan2.kernel = np.ones(record_length, dtype=np.complex).tobytes() - - commit() - return this_receiver - -@db_session -def new_Alazar(label, address, record_length=1024): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=cdb) - chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=cdb) - - this_receiver = Channels.Receiver(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], - record_length=record_length, channel_db=cdb) - this_receiver.trigger_source = "External" - this_receiver.stream_types = "raw" - this_receiver.address = address - - commit() - return this_receiver - -@db_session -def new_qubit(label): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - thing = Channels.Qubit(label=label, channel_db=cdb) - return thing - -@db_session -def new_source(label, model, address, power=-30.0, frequency=5.0e9): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=cdb) - return thing - -@localize_db_objects -def set_control(qubit, transmitter, generator=None): - quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] - markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] - - if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: - raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: - phys_chan = quads[0] - elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): - phys_chan = transmitter - else: - raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - - qubit.phys_chan = phys_chan - if generator: - qubit.phys_chan.generator = generator - -@localize_db_objects -def set_measure(qubit, transmitter, receivers, generator=None, receivers_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] - markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] - - if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: - raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: - phys_chan = quads[0] - elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): - phys_chan = transmitter - else: - raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - - meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=cdb) - meas.phys_chan = phys_chan - if generator: - meas.phys_chan.generator = generator - - phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("12m1") - - trig_chan = Channels.LogicalMarkerChannel(label=f"receiversTrig-{qubit.label}", channel_db=cdb) - trig_chan.phys_chan = phys_trig_channel - trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} - meas.trig_chan = trig_chan - - if isinstance(receivers, Channels.Receiver) and len(receivers.channels) > 1: - raise ValueError("In set_measure the Receiver must have a single receiver channel or a specific channel must be passed instead") - elif isinstance(receivers, Channels.Receiver) and len(receivers.channels) == 1: - rcv_chan = receivers.channels[0] - elif isinstance(receivers, Channels.ReceiverChannel): - rcv_chan = receivers - else: - raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - - meas.receiver_chan = rcv_chan - - if gate: - phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("12m2") - gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=cdb) - gate_chan.phys_chan = phys_gate_channel - meas.gate_chan = gate_chan - -@localize_db_objects -def set_master(transmitter, trig_channel, pulse_length=1e-7): - if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): - raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") - - cdb = Channels.ChannelDatabase[channelLib.channelDatabase.id] # Can't use external object - st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=cdb) - st.phys_chan = trig_channel - st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} - transmitter.master = True - transmitter.trigger_source = "Internal" - -@db_session -def QubitFactory(label, **kwargs): + # # Convenience functions for generating and linking channels + # # TODO: move these to a shim layer shared by Auspex/QGL + # def sessionify(func): + # @wraps(func) + # def without_session(*args, **kwargs): + # return func(channelLib.session, *args, **kwargs) + # return without_session + + def new_APS2(self, label, address): + chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + + this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=self.channelDatabase) + this_transmitter.trigger_source = "external" + this_transmitter.address = address + + self.session.add(this_transmitter) + return this_transmitter + + def new_APS2_rack(self, label, num, start_address): + address_start = ".".join(start_address.split(".")[:3]) + address_end = int(start_address.split(".")[-1]) + transmitters = [new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] + this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase) + + self.session.add(this_transceiver) + return this_transceiver + + def new_X6(self, label, address, dsp_channel=0, record_length=1024): + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=self.channelDatabase) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=self.channelDatabase) + + this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], + record_length=record_length, channel_db=self.channelDatabase) + this_receiver.trigger_source = "external" + this_receiver.stream_types = "raw, demodulated, integrated" + this_receiver.address = address + + # Add a default kernel + chan1.kernel = np.ones(record_length, dtype=np.complex).tobytes() + chan2.kernel = np.ones(record_length, dtype=np.complex).tobytes() + + self.session.add(this_receiver) + return this_receiver + + def new_Alazar(self, label, address, record_length=1024): + chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=self.channelDatabase) + chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=self.channelDatabase) + + this_receiver = Channels.Receiver(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], + record_length=record_length, channel_db=self.channelDatabase) + this_receiver.trigger_source = "external" + this_receiver.stream_types = "raw" + this_receiver.address = address + + self.session.add(this_receiver) + return this_receiver + + def new_qubit(self, label): + thing = Channels.Qubit(label=label, channel_db=self.channelDatabase) + self.session.add(thing) + return thing + + def new_source(self, label, model, address, power=-30.0, frequency=5.0e9): + thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=self.channelDatabase) + self.session.add(thing) + return thing + + # @localize_db_objects + def set_control(self, qubit, transmitter, generator=None): + quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + + if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: + raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: + phys_chan = quads[0] + elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): + phys_chan = transmitter + else: + raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + + qubit.phys_chan = phys_chan + if generator: + qubit.phys_chan.generator = generator + + # @localize_db_objects + def set_measure(self, qubit, transmitter, receivers, generator=None, receivers_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): + quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + + if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: + phys_chan = quads[0] + elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): + phys_chan = transmitter + else: + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + + meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=self.channelDatabase) + meas.phys_chan = phys_chan + if generator: + meas.phys_chan.generator = generator + + phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("12m1") + + trig_chan = Channels.LogicalMarkerChannel(label=f"receiversTrig-{qubit.label}", channel_db=self.channelDatabase) + trig_chan.phys_chan = phys_trig_channel + trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} + meas.trig_chan = trig_chan + + if isinstance(receivers, Channels.Receiver) and len(receivers.channels) > 1: + raise ValueError("In set_measure the Receiver must have a single receiver channel or a specific channel must be passed instead") + elif isinstance(receivers, Channels.Receiver) and len(receivers.channels) == 1: + rcv_chan = receivers.channels[0] + elif isinstance(receivers, Channels.ReceiverChannel): + rcv_chan = receivers + else: + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + + meas.receiver_chan = rcv_chan + + if gate: + phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("12m2") + gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=self.channelDatabase) + gate_chan.phys_chan = phys_gate_channel + meas.gate_chan = gate_chan + + def set_master(self, transmitter, trig_channel, pulse_length=1e-7): + if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): + raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") + + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=self.channelDatabase) + st.phys_chan = trig_channel + st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} + transmitter.master = True + transmitter.trigger_source = "internal" + +def QubitFactory(self, label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' # TODO: this will just get the first entry in the whole damned DB! # thing = select(el for el in Channels.Qubit if el.label==label).first() @@ -648,8 +609,7 @@ def QubitFactory(label, **kwargs): else: return Channels.Qubit(label=label, **kwargs) -@db_session -def MeasFactory(label, **kwargs): +def MeasFactory(self, label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Measurement)}[label] if thing: @@ -657,8 +617,7 @@ def MeasFactory(label, **kwargs): else: return Channels.Measurement(label=label, **kwargs) -@db_session -def MarkerFactory(label, **kwargs): +def MarkerFactory(self, label, **kwargs): ''' Return a saved Marker channel or create a new one. ''' thing = {c.label: c for c in channelLib.get_current_channels()}[label] if thing: @@ -666,8 +625,7 @@ def MarkerFactory(label, **kwargs): else: return Channels.LogicalMarkerChannel(label=label, **kwargs) -@db_session -def EdgeFactory(source, target): +def EdgeFactory(self, source, target): if channelLib.connectivityG.has_edge(source, target): return channelLib.connectivityG[source][target]['channel'] elif channelLib.connectivityG.has_edge(target, source): diff --git a/QGL/Channels.py b/QGL/Channels.py index 2e1a2a8c..1c9807d4 100644 --- a/QGL/Channels.py +++ b/QGL/Channels.py @@ -23,46 +23,4 @@ from . import config from . import PulseShapes -from bbndb import define_entities as def_ent -from bbndb import qgl - -# Get these in global scope for module imports -Channel = None -PhysicalChannel = None -LogicalChannel = None -PhysicalQuadratureChannel = None -PhysicalMarkerChannel = None -LogicalMarkerChannel = None -ReceiverChannel = None -Measurement = None -Qubit = None -Edge = None -MicrowaveSource = None -ChannelDatabase = None -Receiver = None -Transmitter = None -Transceiver = None - -# The main definitions have been moved to bbndb -# to keep the schema consistent across Auspex and QGL -# We retain this local framework in order to maintain -# the paths for QGL primitives - -def define_entities(db, cache_callback=None): - def_ent(db, cache_callback=cache_callback) - - globals()["Channel"] = qgl.Channel - globals()["PhysicalChannel"] = qgl.PhysicalChannel - globals()["LogicalChannel"] = qgl.LogicalChannel - globals()["PhysicalQuadratureChannel"] = qgl.PhysicalQuadratureChannel - globals()["PhysicalMarkerChannel"] = qgl.PhysicalMarkerChannel - globals()["LogicalMarkerChannel"] = qgl.LogicalMarkerChannel - globals()["ReceiverChannel"] = qgl.ReceiverChannel - globals()["Measurement"] = qgl.Measurement - globals()["Qubit"] = qgl.Qubit - globals()["Edge"] = qgl.Edge - globals()["MicrowaveSource"] = qgl.MicrowaveSource - globals()["ChannelDatabase"] = qgl.ChannelDatabase - globals()["Receiver"] = qgl.Receiver - globals()["Transmitter"] = qgl.Transmitter - globals()["Transceiver"] = qgl.Transceiver +from bbndb.qgl import * \ No newline at end of file diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 3ee00996..ec17af38 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -480,8 +480,8 @@ def compile_to_hardware(seqs, extra_meta = awg_metas # create meta output db_info = { - 'db_provider': ChannelLibraries.channelLib.database_provider, - 'db_filename': ChannelLibraries.channelLib.database_file, + 'db_provider': ChannelLibraries.channelLib.db_provider, + 'db_resource_name': ChannelLibraries.channelLib.db_resource_name, 'library_name': 'working', 'library_id': ChannelLibraries.channelLib.channelDatabase.id } diff --git a/QGL/__init__.py b/QGL/__init__.py index b82dc058..aa06418c 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -1,6 +1,6 @@ -from .Channels import Qubit, Measurement, Edge, MicrowaveSource, ChannelDatabase, Transmitter, Receiver, Transceiver +from .Channels import Qubit, Measurement, Edge, Generator, ChannelDatabase, Transmitter, Receiver, Transceiver from .ChannelLibraries import QubitFactory, MeasFactory, EdgeFactory, MarkerFactory, ChannelLibrary, channelLib -from .ChannelLibraries import new_APS2, new_X6, new_APS2_rack, new_Alazar, new_qubit, set_control, set_measure, set_master, new_source +# from .ChannelLibraries import new_APS2, new_X6, new_APS2_rack, new_Alazar, new_qubit, set_control, set_measure, set_master, new_source from .PulsePrimitives import * from .Compiler import compile_to_hardware, set_log_level from .PulseSequencer import align diff --git a/QGL/config.py b/QGL/config.py index eaeb6322..c21dd560 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -6,13 +6,13 @@ import importlib # Where to store AWG data -AWGDir = None +AWGDir = None # The db file, where the channel libraries are stored -db_file = None +db_resource_name = None # The config file (executed upon channel library loading) -config_file = None +config_file = None # plotting options plotBackground = '#EAEAF2' @@ -36,6 +36,7 @@ def load_config(): raise Exception(f"Could not import/execute the BBN_CONFIG {os.getenv('BBN_CONFIG')}") def load_db(): - global db_file + global db_resource_name if os.getenv('BBN_DB'): - db_file = os.getenv("BBN_DB") \ No newline at end of file + db_resource_name = os.getenv("BBN_DB") + return db_resource_name \ No newline at end of file From 352b23c4eb866493fbf1e5a8ca272518442b892c Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 9 Oct 2018 22:05:56 -0400 Subject: [PATCH 064/235] Fix queries for channel db --- QGL/ChannelLibraries.py | 53 ++++++++++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 1f62d3ac..4a369eb2 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -143,7 +143,8 @@ def __init__(self, db_resource_name=None): self.connectivityG = nx.DiGraph() # Check to see whether there is already a temp database - working_dbs = self.session.query(Channels.ChannelDatabase).filter_by(label="working").all() + working_dbs = self.query(Channels.ChannelDatabase, label="working").all() + # working_dbs = self.session.query(Channels.ChannelDatabase).filter_by(label="working").all() if len(working_dbs) > 1: raise Exception("More than one working database exists!") elif len(working_dbs) == 1: @@ -157,6 +158,9 @@ def __init__(self, db_resource_name=None): # Update the global reference channelLib = self + def query(self, obj_type, **kwargs): + return self.session.query(obj_type).filter_by(**kwargs) + def get_current_channels(self): return self.channelDatabase.channels + self.channelDatabase.generators @@ -164,38 +168,47 @@ def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} def ls(self): - select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() - - def ent_by_type_name(self, name, show=False): - q = select(c for c in getattr(bbndb.qgl,name) if c.channel_db.label == "working") + cdb = Channels.ChannelDatabase + q = self.session.query(cdb.label, cdb.time, cdb.id).\ + order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label).all() + # select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() + for i, (label, time, id) in enumerate(q): + print(f"[{id}] ({time}) -> {label}") + + def ent_by_type(self, obj_type, show=False): + q = self.session.query(obj_type).filter(obj_type.channel_db.has(label="working")).order_by(obj_type.label).all() if show: - select(c.label for c in getattr(bbndb.qgl,name) if c.channel_db.label == "working").sort_by(1).show() + for i, el in enumerate(q): + print(f"[{i}] -> {el.label}") else: - return {el.label: el for el in q} + return q def receivers(self): - return self.ent_by_type_name("Receiver") + return self.ent_by_type(Channels.Receiver) + + def transmitters(self): + return self.ent_by_type(Channels.Transmitter) - def transmitter(self): - return self.ent_by_type_name("Transmitter") + def transceivers(self): + return self.ent_by_type(Channels.Transceiver) def qubits(self): - return self.ent_by_type_name("Qubit") + return self.ent_by_type(Channels.Qubit) def meas(self): - return self.ent_by_type_name("Measurement") + return self.ent_by_type(Channels.Measurement) def ls_receivers(self): - return self.ent_by_type_name("Receiver", show=True) + return self.ent_by_type(Channels.Receiver, show=True) def ls_transmitters(self): - return self.ent_by_type_name("Transmitter", show=True) + return self.ent_by_type(Channels.Transmitter, show=True) def ls_qubits(self): - return self.ent_by_type_name("Qubit", show=True) + return self.ent_by_type(Channels.Qubit, show=True) - def ls_meas(self): - return self.ent_by_type_name("Measurement", show=True) + def ls_measurements(self): + return self.ent_by_type(Channels.Measurement, show=True) def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ @@ -225,6 +238,12 @@ def load_obj(self, obj): copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=self.channelDatabase) self.update_channelDict() + def save(self): + self.session.commit() + + def discard_changes(self): + self.session.rollback() + def save_as(self, name): chans, srcs, d2as, a2dsm, trans = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, self.channelDatabase.transmitters, self.channelDatabase.receivers, From 64cca256c3b480558b9e0ff7fa486e28b89d054f Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 9 Oct 2018 22:22:10 -0400 Subject: [PATCH 065/235] Fix Factories in ChannelLibraries --- QGL/ChannelLibraries.py | 49 +++++++++++++++++++++-------------------- 1 file changed, 25 insertions(+), 24 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 4a369eb2..575d28db 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -476,14 +476,6 @@ def load_from_library(self, return_only=False): self.connectivityG[chan.source][chan.target]['channel'] = chan >>>>>>> Ditch atom, move to Pony.orm for all channel library objects. - # # Convenience functions for generating and linking channels - # # TODO: move these to a shim layer shared by Auspex/QGL - # def sessionify(func): - # @wraps(func) - # def without_session(*args, **kwargs): - # return func(channelLib.session, *args, **kwargs) - # return without_session - def new_APS2(self, label, address): chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) @@ -618,33 +610,42 @@ def set_master(self, transmitter, trig_channel, pulse_length=1e-7): transmitter.master = True transmitter.trigger_source = "internal" -def QubitFactory(self, label, **kwargs): +def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' # TODO: this will just get the first entry in the whole damned DB! # thing = select(el for el in Channels.Qubit if el.label==label).first() - thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Qubit)}[label] - if thing: - return thing + q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label).all() + # thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Qubit)}[label] + if len(q) == 1: + return q[0] else: - return Channels.Qubit(label=label, **kwargs) + c = Channels.Qubit(label=label, channel_db=channelLib.channelDatabase, **kwargs) + channelLib.session.add(c) + return c -def MeasFactory(self, label, **kwargs): +def MeasFactory(label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' - thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Measurement)}[label] - if thing: - return thing + q = channelLib.session.query(Channels.Measurement).filter(Channels.Measurement.label==label).all() + # thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Measurement)}[label] + if len(q) == 1: + return q[0] else: - return Channels.Measurement(label=label, **kwargs) + c = Channels.Measurement(label=label, channel_db=channelLib.channelDatabase, **kwargs) + channelLib.session.add(c) + return c -def MarkerFactory(self, label, **kwargs): +def MarkerFactory(label, **kwargs): ''' Return a saved Marker channel or create a new one. ''' - thing = {c.label: c for c in channelLib.get_current_channels()}[label] - if thing: - return thing + q = channelLib.session.query(Channels.LogicalMarkerChannel).filter(Channels.LogicalMarkerChannel.label==label).all() + # thing = {c.label: c for c in channelLib.get_current_channels()}[label] + if len(q) == 1: + return q[0] else: - return Channels.LogicalMarkerChannel(label=label, **kwargs) + c = Channels.LogicalMarkerChannel(label=label, channel_db=channelLib.channelDatabase, **kwargs) + channelLib.session.add(c) + return c -def EdgeFactory(self, source, target): +def EdgeFactory(source, target): if channelLib.connectivityG.has_edge(source, target): return channelLib.connectivityG[source][target]['channel'] elif channelLib.connectivityG.has_edge(target, source): From c3279f6b7c99f624e779196431a92b4f40fb0ba7 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 11 Oct 2018 09:51:57 -0400 Subject: [PATCH 066/235] Clearing now functioning properly, working on copy, load --- QGL/ChannelLibraries.py | 329 +++++++++------------------------------- 1 file changed, 73 insertions(+), 256 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 575d28db..995191ac 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -40,7 +40,7 @@ from functools import wraps import numpy as np import networkx as nx - + import bbndb from . import config @@ -48,21 +48,20 @@ from . import PulseShapes from .PulsePrimitives import clear_pulse_cache +from sqlalchemy.orm.session import make_transient + channelLib = None -# def localize_db_objects(f): - # """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" - # @wraps(f) - # def wrapper(*args, **kwargs): - # global db - # with db_session: - # args_info = [(type(arg), arg.id) if isinstance(arg, db.Entity) else (arg, None) for arg in args] - # kwargs_info = {k: (type(v), v.id) if isinstance(v, db.Entity) else (v, None) for k, v in kwargs.items()} - # with db_session: - # new_args = [c[i] if i else c for c, i in args_info] - # new_kwargs = {k: v[0][v[1]] if v[1] else v[0] for k,v in kwargs_info.items()} - # return f(*new_args, **new_kwargs) - # return wrapper +def check_session_dirty(f): + """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" + @wraps(f) + def wrapper(cls, *args, **kwargs): + if not 'force' in kwargs and kwargs['force']: + if len(cls.session.dirty | cls.session.new) != 0: + kwargs.pop('force') + raise Exception("Uncommitted transactions for working database. Either use force=True or commit/revert your changes.") + return f(cls, *args, **kwargs) + return wrapper def set_from_dict(self, obj, settings): for prop_name in obj.to_dict().keys(): @@ -72,7 +71,7 @@ def set_from_dict(self, obj, settings): except Exception as e: print(f"{obj.label}: Error loading {prop_name} from config") -def copy_objs(*entities, new_channel_db): +def copy_objs(entities, new_channel_db, session): # Entities is a list of lists of entities of specific types new_entities = [] old_to_new = {} @@ -81,7 +80,7 @@ def copy_objs(*entities, new_channel_db): for ent in entities: new_ents = [] for obj in ent: - c, links = copy_entity(obj, new_channel_db) + c, links = copy_entity(obj, new_channel_db, session) new_ents.append(c) links_to_change[c] = links old_to_new[c.label] = c @@ -89,29 +88,44 @@ def copy_objs(*entities, new_channel_db): for chan, link_info in links_to_change.items(): for attr_name, link_name in link_info.items(): - if isinstance(link_name, pony.orm.core.Multiset): + if isinstance(link_name, list): new = [old_to_new[ln] for ln in link_name] else: new = old_to_new[link_name] setattr(chan, attr_name, new) + session.add_all(entities + [new_channel_db]) return new_entities -def copy_entity(obj, new_channel_db): +def copy_entity(obj, new_channel_db, session): """Copy a pony entity instance""" - kwargs = {a.name: getattr(obj, a.name) for a in obj._attrs_ if a.name not in ["id", "classtype"]} + rel_vals = {} + + # Make a copy of the relationships + for rel_name in obj.__mapper__.relationships.keys(): + attr = getattr(obj, rel_name) + print("examining", rel_name, attr.__class__) + if isinstance(attr, list): + print("adding", rel_name) + rel_vals[rel_name] = attr.copy() + + # Expire these relationships from the session + session.expire(b) + + # Make the object instances transient + make_transient(obj) - # Extract any links to other entities - links = {} - for attr in obj._attrs_: - if attr.name not in ["channel_db"]: - obj_attr = getattr(obj, attr.name) - if hasattr(obj_attr, "id"): - kwargs.pop(attr.name) - links[attr.name] = obj_attr.label + # Restore the old links + for rel_name, rel_val in rel_vals.items(): + setattr(obj, rel_name, rel_val) - kwargs["channel_db"] = new_channel_db - return obj.__class__(**kwargs), links + # Reset the id and channel db + obj.id = None + obj.channel_db = new_channel_db + + session.add(obj) + + return obj, rel_vals class ChannelLibrary(object): @@ -127,14 +141,14 @@ def __init__(self, db_resource_name=None): # Use current db self.db = bbndb.engine else: - + if db_resource_name: self.db_resource_name = db_resource_name elif config.load_db(): self.db_resource_name = config.load_db() self.db = bbndb.engine = bbndb.create_engine(f'{self.db_provider}:///{self.db_resource_name}', echo=False) - + bbndb.Base.metadata.create_all(bbndb.engine) bbndb.Session.configure(bind=bbndb.engine) self.Session = bbndb.Session @@ -152,6 +166,7 @@ def __init__(self, db_resource_name=None): elif len(working_dbs) == 0: self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) self.session.add(self.channelDatabase) + self.session.commit() self.update_channelDict() @@ -171,7 +186,6 @@ def ls(self): cdb = Channels.ChannelDatabase q = self.session.query(cdb.label, cdb.time, cdb.id).\ order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label).all() - # select((c.label, c.time, c.id) for c in Channels.ChannelDatabase).sort_by(1, 2).show() for i, (label, time, id) in enumerate(q): print(f"[{id}] ({time}) -> {label}") @@ -210,46 +224,51 @@ def ls_qubits(self): def ls_measurements(self): return self.ent_by_type(Channels.Measurement, show=True) + @check_session_dirty def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ - obj = list(select(c for c in Channels.ChannelDatabase if c.label==name).sort_by(desc(Channels.ChannelDatabase.time))) - self.load_obj(obj[-index]) + cdb = Channels.ChannelDatabase + items = self.session.query(cdb).filter(cdb.label==name).order_by(cdb.time.desc()).all() + self.load_obj(items[-index]) + @check_session_dirty def load_by_id(self, id_num): - obj = select(c for c in Channels.ChannelDatabase if c.id==id_num).first() - self.load_obj(obj) + cdb = Channels.ChannelDatabase + item = self.session.query(cdb).filter(cdb.id==id_num).first() + self.load_obj(item) def clear(self, channel_db=None, create_new=True): # If no database is specified, clear self.database channel_db = channel_db if channel_db else self.channelDatabase - # First clear items that don't have Sets of other items - for ent in [Channels.MicrowaveSource, Channels.Channel, Channels.Transmitter, Channels.Receiver, Channels.Transceiver]: - select(c for c in ent if c.channel_db == channel_db).delete(bulk=True) - # Now clear items that do potentially have sets of items (which should be deleted) - for ent in [Channels.ChannelDatabase]: - select(d for d in ent if d.label == "working").delete(bulk=True) + + self.session.delete(channel_db) + self.session.commit() + if create_new: - print("created new database with id", self.channelDatabase.id) + self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) + self.session.add(self.channelDatabase) + self.session.commit() channelLib = self def load_obj(self, obj): self.clear() - chans, srcs, d2as, a2ds, trans = map(list, [obj.channels, obj.sources, obj.transmitters, obj.receivers, obj.transceivers]) - copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=self.channelDatabase) + chans, srcs, d2as, a2ds, trans = map(list, [obj.channels, obj.generators, obj.transmitters, obj.receivers, obj.transceivers]) + copy_objs([chans, srcs, d2as, a2ds, trans], self.channelDatabase, self.session) self.update_channelDict() - def save(self): + def commit(self): self.session.commit() - def discard_changes(self): + def revert(self): self.session.rollback() def save_as(self, name): - chans, srcs, d2as, a2dsm, trans = map(list, [self.channelDatabase.channels, self.channelDatabase.sources, + self.commit() + chans, srcs, d2as, a2ds, trans = map(list, [self.channelDatabase.channels, self.channelDatabase.generators, self.channelDatabase.transmitters, self.channelDatabase.receivers, self.channelDatabase.transceivers]) - new_chans, new_srcs, new_d2as, new_a2ds, new_trans = copy_objs(chans, srcs, d2as, a2ds, trans, new_channel_db=cd) - cd.channels, cd.sources, cd.transmitters, cd.receivers, cd.transceivers = new_chans, new_srcs, new_d2as, new_a2ds, new_trans + new_chans, new_srcs, new_d2as, new_a2ds, new_trans = copy_objs([chans, srcs, d2as, a2ds, trans], cd, self.session) + cd.channels, cd.generators, cd.transmitters, cd.receivers, cd.transceivers = new_chans, new_srcs, new_d2as, new_a2ds, new_trans #Dictionary methods def __getitem__(self, key): @@ -272,206 +291,11 @@ def values(self): def build_connectivity_graph(self): # build connectivity graph -<<<<<<< HEAD - self.connectivityG.clear() - for chan in self.channelDict.values(): - if isinstance(chan, - Channels.Qubit) and chan not in self.connectivityG: - self.connectivityG.add_node(chan) - for chan in self.channelDict.values(): - if isinstance(chan, Channels.Edge): - self.connectivityG.add_edge(chan.source, chan.target) - self.connectivityG[chan.source][chan.target]['channel'] = chan - - def load_from_library(self, return_only=False): - """Loads the YAML library, creates the QGL objects, and returns a list of the visited filenames - for the filewatcher.""" - if not self.library_file: - return - try: - with open(self.library_file, 'r') as FID: - loader = config.Loader(FID) - try: - tmpLib = loader.get_single_data() - filenames = loader.filenames - finally: - loader.dispose() - - # Check to see if we have the mandatory sections - for section in ['instruments', 'qubits']: #, 'filters']: - if section not in tmpLib.keys(): - raise ValueError("{} section not present in config file {}.".format(section, self.library_file)) - - instr_dict = tmpLib['instruments'] - qubit_dict = tmpLib['qubits'] - # filter_dict = tmpLib['filters'] - trigger_dict = tmpLib.get('markers', {}) # This section is optional - edge_dict = tmpLib.get('edges', {}) # This section is optional - master_awgs = [] - - # Construct the channel library - channel_dict = {} - marker_lens = {} - - for name, instr in instr_dict.items(): - if "tx_channels" in instr.keys(): - for chan_name, channel in instr["tx_channels"].items(): - if channel is None: - params = {} - else: - params = {k: v for k,v in channel.items() if k in Channels.PhysicalQuadratureChannel.__atom_members__.keys()} - params["label"] = name + "-" + chan_name - params["instrument"] = name - params["translator"] = instr["type"] + "Pattern" - params["__module__"] = "QGL.Channels" - params["__class__"] = "PhysicalQuadratureChannel" - channel_dict[params["label"]] = params - if "rx_channels" in instr.keys(): - for chan_name, channel in instr["rx_channels"].items(): - if channel is None: - params = {} - else: - params = {k: v for k,v in channel.items() if k in Channels.PhysicalMarkerChannel.__atom_members__.keys()} - params["label"] = name + "-" + chan_name - params["instrument"] = name - params["translator"] = instr["type"] + "Pattern" - params["__module__"] = "QGL.Channels" - params["__class__"] = "PhysicalMarkerChannel" - channel_dict[params["label"]] = params - if "markers" in instr.keys(): - for mark_name, marker in instr["markers"].items(): - if marker is None: - params = {} - else: - params = {k: v for k,v in marker.items() if k in Channels.PhysicalMarkerChannel.__atom_members__.keys()} - params["label"] = name + "-" + mark_name - params["instrument"] = name - params["translator"] = instr["type"] + "Pattern" - params["__module__"] = "QGL.Channels" - params["__class__"] = "PhysicalMarkerChannel" - if "length" in marker.keys(): - marker_lens[params["label"]] = marker["length"] - channel_dict[params["label"]] = params - if "master" in instr.keys() and instr["master"]: - if instr['type'] != 'TDM': - slave_chan = instr["slave_trig"] if "slave_trig" in instr.keys() else "slave" - master_awgs.append(name + "-" + slave_chan) - else: - master_awgs.append(name) - # Eventually we should support multiple masters... - if "slave_trig" in instr.keys(): - params = {} - params["label"] = "slave_trig" - params["phys_chan"] = name + "-" + instr["slave_trig"] - if params["phys_chan"] in marker_lens.keys(): - length = marker_lens[params["phys_chan"]] - else: - length = 1e-7 - params["pulse_params"] = {"length": length, "shape_fun": "constant"} - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - channel_dict[params["label"]] = params - - # Establish the slave trigger, assuming for now that we have a single - # APS master. This might change later. - if len(master_awgs) > 1: - raise ValueError("More than one AWG is marked as master.") - # elif len(master_awgs) == 1 and instr_dict[master_awgs[0].split('-')[0]]['type'] != 'TDM': - # params = {} - # params["label"] = "slave_trig" - # params["phys_chan"] = master_awgs[0] - # if params["phys_chan"] in marker_lens.keys(): - # length = marker_lens[params["phys_chan"]] - # else: - # length = 1e-7 - # params["pulse_params"] = {"length": length, "shape_fun": "constant"} - # params["__module__"] = "QGL.Channels" - # params["__class__"] = "LogicalMarkerChannel" - # channel_dict[params["label"]] = params - - # for name, filt in filter_dict.items(): - # if "StreamSelector" in filt["type"]: - # params = {k: v for k,v in filt.items() if k in Channels.ReceiverChannel.__atom_members__.keys()} - # params["label"] = "RecvChan-" + name # instr_dict[filt["instrument"]]["name"] + "-" + name - # params["channel"] = str(params["channel"]) # Convert to a string - # params["instrument"] = filt["source"] - # params["__module__"] = "QGL.Channels" - # params["__class__"] = "ReceiverChannel" - # if "source" not in filt.keys(): - # raise ValueError("No instrument (source) specified for Stream Selector") - # if filt["source"] not in instr_dict.keys() and filt["source"] not in channel_dict.keys(): - # raise ValueError("Stream Selector source {} not found among list of instruments.".format(filt["source"])) - # params["instrument"] = filt["source"] - - # channel_dict[params["label"]] = params - - for name, qubit in qubit_dict.items(): - # Create a stream selector - rcv_inst, rcv_chan, rcv_stream = qubit["measure"]["receiver"].split() - rcv_params = {} - rcv_params["label"] = "RecvChan-" + name + "-SS" - rcv_params["channel"] = str(rcv_chan) - rcv_params["instrument"] = rcv_inst - rcv_params["__module__"] = "QGL.Channels" - rcv_params["__class__"] = "ReceiverChannel" - channel_dict[rcv_params["label"]] = rcv_params - - # Create the Qubits - if len(qubit["control"]["AWG"].split()) != 2: - print("Control AWG specification for {} ({}) must have a device, channel".format(name, qubit["control"]["AWG"])) - raise ValueError("Control AWG specification for {} ({}) must have a device, channel".format(name, qubit["control"]["AWG"])) - ctrl_instr, ctrl_chan = qubit["control"]["AWG"].split() - params = {k: v for k,v in qubit["control"].items() if k in Channels.Qubit.__atom_members__.keys()} - params["label"] = name - params["phys_chan"] = ctrl_instr + "-" + ctrl_chan - params["__module__"] = "QGL.Channels" - params["__class__"] = "Qubit" - channel_dict[params["label"]] = params - if 'generator' in qubit["control"].keys(): - channel_dict[params["phys_chan"]]["generator"] = qubit["control"]["generator"] - - # Create the measurements - if len(qubit["measure"]["AWG"].split()) != 2: - print("Measurement AWG specification for {} ({}) must have a device, channel".format(name, qubit["measure"]["AWG"])) - raise ValueError("Measurement AWG specification for {} ({}) must have a device, channel".format(name, qubit["measure"]["AWG"])) - meas_instr, meas_chan = qubit["measure"]["AWG"].split() - params = {k: v for k,v in qubit["measure"].items() if k in Channels.Measurement.__atom_members__.keys()} - params["label"] = "M-{}".format(name) - # parse the digitizer trigger from the marker dictionary, if available. If not, expected in the form Instr Ch - dig_trig = trigger_dict.get(qubit["measure"]["trigger"], qubit["measure"]["trigger"]) if trigger_dict else qubit["measure"]["trigger"] - params["trig_chan"] = "digTrig-" + dig_trig - params["phys_chan"] = meas_instr + "-" + meas_chan - params["meas_type"] = "autodyne" - params["receiver_chan"] = rcv_params["label"] - params["__module__"] = "QGL.Channels" - params["__class__"] = "Measurement" - channel_dict[params["label"]] = params - if 'generator' in qubit["measure"].keys(): - channel_dict[params["phys_chan"]]["generator"] = qubit["measure"]["generator"] - - # Create the receiver channels - if "receiver" in qubit["measure"].keys(): - if len(qubit["measure"]["receiver"].split()) != 3: - print("Receiver specification for {} ({}) must have an instrument name, physical channel, and stream".format(name, qubit["measure"]["receiver"])) - raise ValueError("Receiver specification for {} ({}) must have a stream selector".format(name, qubit["measure"]["receiver"])) - phys_instr, phys_marker = dig_trig.split() - params = {} - params["label"] = "digTrig-" + dig_trig - params["phys_chan"] = phys_instr + "-" + phys_marker - if params["phys_chan"] in marker_lens.keys(): - length = marker_lens[params["phys_chan"]] - else: - length = 3e-7 - params["pulse_params"] = {"length": length, "shape_fun": "constant"} - params["__module__"] = "QGL.Channels" - params["__class__"] = "LogicalMarkerChannel" - # Don't duplicate triggers to the same digitizer - if params["label"] not in channel_dict.keys(): - channel_dict[params["label"]] = params -======= for chan in select(q for q in Channels.Qubit if q not in self.connectivityG): +======= + for chan in self.session.query(Channels.Qubit).filter(Channels.Qubit not in self.connectivityG).all(): self.connectivityG.add_node(chan) - for chan in select(e for e in Channels.Edge): + for chan in self.session.query(Channels.Edge): #select(e for e in Channels.Edge): self.connectivityG.add_edge(chan.source, chan.target) self.connectivityG[chan.source][chan.target]['channel'] = chan >>>>>>> Ditch atom, move to Pony.orm for all channel library objects. @@ -535,11 +359,10 @@ def new_qubit(self, label): return thing def new_source(self, label, model, address, power=-30.0, frequency=5.0e9): - thing = Channels.MicrowaveSource(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=self.channelDatabase) + thing = Channels.Generator(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=self.channelDatabase) self.session.add(thing) return thing - # @localize_db_objects def set_control(self, qubit, transmitter, generator=None): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -557,7 +380,6 @@ def set_control(self, qubit, transmitter, generator=None): if generator: qubit.phys_chan.generator = generator - # @localize_db_objects def set_measure(self, qubit, transmitter, receivers, generator=None, receivers_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -612,10 +434,7 @@ def set_master(self, transmitter, trig_channel, pulse_length=1e-7): def QubitFactory(label, **kwargs): ''' Return a saved qubit channel or create a new one. ''' - # TODO: this will just get the first entry in the whole damned DB! - # thing = select(el for el in Channels.Qubit if el.label==label).first() q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label).all() - # thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Qubit)}[label] if len(q) == 1: return q[0] else: @@ -626,7 +445,6 @@ def QubitFactory(label, **kwargs): def MeasFactory(label, **kwargs): ''' Return a saved measurement channel or create a new one. ''' q = channelLib.session.query(Channels.Measurement).filter(Channels.Measurement.label==label).all() - # thing = {c.label: c for c in channelLib.get_current_channels() if isinstance(c, Channels.Measurement)}[label] if len(q) == 1: return q[0] else: @@ -637,7 +455,6 @@ def MeasFactory(label, **kwargs): def MarkerFactory(label, **kwargs): ''' Return a saved Marker channel or create a new one. ''' q = channelLib.session.query(Channels.LogicalMarkerChannel).filter(Channels.LogicalMarkerChannel.label==label).all() - # thing = {c.label: c for c in channelLib.get_current_channels()}[label] if len(q) == 1: return q[0] else: From f79129f925a9efc97824f0b1e7209735397bbf95 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 11 Oct 2018 22:16:57 -0400 Subject: [PATCH 067/235] Move to cleaner deep copy and fix up saving channel dbs --- QGL/ChannelLibraries.py | 158 ++++++++++++++++++++++------------------ 1 file changed, 88 insertions(+), 70 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 995191ac..c5ecf172 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -49,6 +49,7 @@ from .PulsePrimitives import clear_pulse_cache from sqlalchemy.orm.session import make_transient +from sqlalchemy.pool import StaticPool channelLib = None @@ -63,69 +64,84 @@ def wrapper(cls, *args, **kwargs): return f(cls, *args, **kwargs) return wrapper -def set_from_dict(self, obj, settings): - for prop_name in obj.to_dict().keys(): - if prop_name in settings.keys(): - try: - setattr(obj, prop_name, settings[prop_name]) - except Exception as e: - print(f"{obj.label}: Error loading {prop_name} from config") - -def copy_objs(entities, new_channel_db, session): - # Entities is a list of lists of entities of specific types - new_entities = [] - old_to_new = {} - links_to_change = {} - - for ent in entities: - new_ents = [] - for obj in ent: - c, links = copy_entity(obj, new_channel_db, session) - new_ents.append(c) - links_to_change[c] = links - old_to_new[c.label] = c - new_entities.append(new_ents) - - for chan, link_info in links_to_change.items(): - for attr_name, link_name in link_info.items(): - if isinstance(link_name, list): - new = [old_to_new[ln] for ln in link_name] - else: - new = old_to_new[link_name] - setattr(chan, attr_name, new) - - session.add_all(entities + [new_channel_db]) - return new_entities - -def copy_entity(obj, new_channel_db, session): - """Copy a pony entity instance""" - rel_vals = {} - - # Make a copy of the relationships - for rel_name in obj.__mapper__.relationships.keys(): - attr = getattr(obj, rel_name) - print("examining", rel_name, attr.__class__) - if isinstance(attr, list): - print("adding", rel_name) - rel_vals[rel_name] = attr.copy() - - # Expire these relationships from the session - session.expire(b) - - # Make the object instances transient - make_transient(obj) - - # Restore the old links - for rel_name, rel_val in rel_vals.items(): - setattr(obj, rel_name, rel_val) - - # Reset the id and channel db - obj.id = None - obj.channel_db = new_channel_db - - session.add(obj) - - return obj, rel_vals +# def set_from_dict(self, obj, settings): +# for prop_name in obj.to_dict().keys(): +# if prop_name in settings.keys(): +# try: +# setattr(obj, prop_name, settings[prop_name]) +# except Exception as e: +# print(f"{obj.label}: Error loading {prop_name} from config") + + +# def copy_objs(entities, new_channel_db, session): +# # Entities is a list of lists of entities of specific types +# new_entities = [] +# old_to_new = {} +# links_to_change = {} + +# for ent in entities: +# new_ents = [] +# for obj in ent: +# c, links = copy_entity(obj, new_channel_db, session) +# new_ents.append(c) +# links_to_change[c] = links +# old_to_new[c.label] = c +# new_entities.append(new_ents) +# # import ipdb; ipdb.set_trace() +# # for chan, link_info in links_to_change.items(): +# # for attr_name, link_name in link_info.items(): +# # if isinstance(link_name, list): +# # new = [old_to_new[ln] for ln in link_name] +# # else: +# # new = old_to_new[link_name] +# # setattr(chan, attr_name, new) +# # import ipdb; ipdb.set_trace() +# for ents in new_entities: +# session.add_all(ents) +# session.add(new_channel_db) +# return new_entities + +# def copy_entity(obj, new_channel_db, session): +# """Copy a pony entity instance""" +# rel_vals = {} +# to_relink = [] + +# # Make a copy of the relationships +# print("Copying object", obj.label) +# for rel_name in obj.__mapper__.attrs.keys(): +# attr = getattr(obj, rel_name) +# # print("\texamining", rel_name, attr.__class__) +# if isinstance(attr, list): +# rel_vals[rel_name] = attr.copy() +# # to_relink.append(rel_name) +# else: +# rel_vals[rel_name] = attr + +# # for rel_name in obj.__mapper__.attrs.keys(): + +# # Expire these relationships from the session +# # print("to_relink", to_relink) +# # session.expire(obj, to_relink) +# # print("\tNew label", obj.label) + +# # Make the object instances transient +# make_transient(obj) +# # print("\tNew label", obj.label) + +# # Restore the old links +# for rel_name, rel_val in rel_vals.items(): +# # print("\tsetting", rel_name, rel_val) +# setattr(obj, rel_name, rel_val) +# # print("\tNew label", obj.label) + +# # Reset the id and channel db +# obj.id = None +# obj.channel_db = new_channel_db +# # print("\tNew label", obj.label) + +# session.add(obj) + +# return obj, rel_vals class ChannelLibrary(object): @@ -147,7 +163,10 @@ def __init__(self, db_resource_name=None): elif config.load_db(): self.db_resource_name = config.load_db() - self.db = bbndb.engine = bbndb.create_engine(f'{self.db_provider}:///{self.db_resource_name}', echo=False) + self.db = bbndb.engine = bbndb.create_engine(f'{self.db_provider}:///{self.db_resource_name}', + connect_args={'check_same_thread':False}, + poolclass=StaticPool, + echo=False) bbndb.Base.metadata.create_all(bbndb.engine) bbndb.Session.configure(bind=bbndb.engine) @@ -264,11 +283,10 @@ def revert(self): def save_as(self, name): self.commit() - chans, srcs, d2as, a2ds, trans = map(list, [self.channelDatabase.channels, self.channelDatabase.generators, - self.channelDatabase.transmitters, self.channelDatabase.receivers, - self.channelDatabase.transceivers]) - new_chans, new_srcs, new_d2as, new_a2ds, new_trans = copy_objs([chans, srcs, d2as, a2ds, trans], cd, self.session) - cd.channels, cd.generators, cd.transmitters, cd.receivers, cd.transceivers = new_chans, new_srcs, new_d2as, new_a2ds, new_trans + new_channelDatabase = bbndb.deepcopy_sqla_object(self.channelDatabase, self.session) + new_channelDatabase.label = name + new_channelDatabase.time = datetime.datetime.now() + self.commit() #Dictionary methods def __getitem__(self, key): @@ -380,7 +398,7 @@ def set_control(self, qubit, transmitter, generator=None): if generator: qubit.phys_chan.generator = generator - def set_measure(self, qubit, transmitter, receivers, generator=None, receivers_channel=1, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): + def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] From 7feae774c953f5831aeda1dd9248390f69d45dc6 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 11 Oct 2018 22:31:06 -0400 Subject: [PATCH 068/235] Saving and loading appear to be working again --- QGL/ChannelLibraries.py | 97 ++++------------------------------------- 1 file changed, 8 insertions(+), 89 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index c5ecf172..56ccd3ad 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -8,7 +8,7 @@ Original Author: Colm Ryan Modified By: Graham Rowlands -Copyright 2016 Raytheon BBN Technologies +Copyright 2016-2018 Raytheon BBN Technologies Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -57,92 +57,12 @@ def check_session_dirty(f): """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" @wraps(f) def wrapper(cls, *args, **kwargs): - if not 'force' in kwargs and kwargs['force']: - if len(cls.session.dirty | cls.session.new) != 0: - kwargs.pop('force') - raise Exception("Uncommitted transactions for working database. Either use force=True or commit/revert your changes.") - return f(cls, *args, **kwargs) + if 'force' in kwargs and kwargs['force']: + return f(cls, *args, **kwargs) + elif len(cls.session.dirty | cls.session.new) != 0: + raise Exception("Uncommitted transactions for working database. Either use force=True or commit/revert your changes.") return wrapper -# def set_from_dict(self, obj, settings): -# for prop_name in obj.to_dict().keys(): -# if prop_name in settings.keys(): -# try: -# setattr(obj, prop_name, settings[prop_name]) -# except Exception as e: -# print(f"{obj.label}: Error loading {prop_name} from config") - - -# def copy_objs(entities, new_channel_db, session): -# # Entities is a list of lists of entities of specific types -# new_entities = [] -# old_to_new = {} -# links_to_change = {} - -# for ent in entities: -# new_ents = [] -# for obj in ent: -# c, links = copy_entity(obj, new_channel_db, session) -# new_ents.append(c) -# links_to_change[c] = links -# old_to_new[c.label] = c -# new_entities.append(new_ents) -# # import ipdb; ipdb.set_trace() -# # for chan, link_info in links_to_change.items(): -# # for attr_name, link_name in link_info.items(): -# # if isinstance(link_name, list): -# # new = [old_to_new[ln] for ln in link_name] -# # else: -# # new = old_to_new[link_name] -# # setattr(chan, attr_name, new) -# # import ipdb; ipdb.set_trace() -# for ents in new_entities: -# session.add_all(ents) -# session.add(new_channel_db) -# return new_entities - -# def copy_entity(obj, new_channel_db, session): -# """Copy a pony entity instance""" -# rel_vals = {} -# to_relink = [] - -# # Make a copy of the relationships -# print("Copying object", obj.label) -# for rel_name in obj.__mapper__.attrs.keys(): -# attr = getattr(obj, rel_name) -# # print("\texamining", rel_name, attr.__class__) -# if isinstance(attr, list): -# rel_vals[rel_name] = attr.copy() -# # to_relink.append(rel_name) -# else: -# rel_vals[rel_name] = attr - -# # for rel_name in obj.__mapper__.attrs.keys(): - -# # Expire these relationships from the session -# # print("to_relink", to_relink) -# # session.expire(obj, to_relink) -# # print("\tNew label", obj.label) - -# # Make the object instances transient -# make_transient(obj) -# # print("\tNew label", obj.label) - -# # Restore the old links -# for rel_name, rel_val in rel_vals.items(): -# # print("\tsetting", rel_name, rel_val) -# setattr(obj, rel_name, rel_val) -# # print("\tNew label", obj.label) - -# # Reset the id and channel db -# obj.id = None -# obj.channel_db = new_channel_db -# # print("\tNew label", obj.label) - -# session.add(obj) - -# return obj, rel_vals - class ChannelLibrary(object): def __init__(self, db_resource_name=None): @@ -177,7 +97,6 @@ def __init__(self, db_resource_name=None): # Check to see whether there is already a temp database working_dbs = self.query(Channels.ChannelDatabase, label="working").all() - # working_dbs = self.session.query(Channels.ChannelDatabase).filter_by(label="working").all() if len(working_dbs) > 1: raise Exception("More than one working database exists!") elif len(working_dbs) == 1: @@ -270,9 +189,9 @@ def clear(self, channel_db=None, create_new=True): channelLib = self def load_obj(self, obj): - self.clear() - chans, srcs, d2as, a2ds, trans = map(list, [obj.channels, obj.generators, obj.transmitters, obj.receivers, obj.transceivers]) - copy_objs([chans, srcs, d2as, a2ds, trans], self.channelDatabase, self.session) + self.clear(create_new=False) + self.channelDatabase = bbndb.deepcopy_sqla_object(obj, self.session) + self.channelDatabase.label = "working" self.update_channelDict() def commit(self): From 4fc5cfd2d81c093f871f7eea5d7eda58fddf9653 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 11 Oct 2018 23:20:55 -0400 Subject: [PATCH 069/235] Compatibility of compiler with new SQLAlchemy --- QGL/ChannelLibraries.py | 7 +++++-- QGL/PatternUtils.py | 2 +- QGL/config.py | 2 +- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 56ccd3ad..338766cf 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -3,7 +3,7 @@ real channels. Split from Channels.py on Jan 14, 2016. -Moved to pony ORM from atom June 1, 2018 +Moved to SQLAlchemy ORM from atom 2018 Original Author: Colm Ryan Modified By: Graham Rowlands @@ -125,7 +125,8 @@ def ls(self): q = self.session.query(cdb.label, cdb.time, cdb.id).\ order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label).all() for i, (label, time, id) in enumerate(q): - print(f"[{id}] ({time}) -> {label}") + t = time.strftime("(%Y) %b. %d @ %I:%M:%S %p") + print(f"[{id}] {t} -> {label}") def ent_by_type(self, obj_type, show=False): q = self.session.query(obj_type).filter(obj_type.channel_db.has(label="working")).order_by(obj_type.label).all() @@ -201,6 +202,8 @@ def revert(self): self.session.rollback() def save_as(self, name): + if name == "working": + raise ValueError("Cannot save as `working` since that is the default working environment name...") self.commit() new_channelDatabase = bbndb.deepcopy_sqla_object(self.channelDatabase, self.session) new_channelDatabase.label = name diff --git a/QGL/PatternUtils.py b/QGL/PatternUtils.py index 9748b35d..a718082a 100644 --- a/QGL/PatternUtils.py +++ b/QGL/PatternUtils.py @@ -202,7 +202,7 @@ def add_digitizer_trigger(seqs): #find corresponding digitizer trigger chanlist = list(flatten([seq[ct].channel])) for chan in chanlist: - if hasattr(chan, 'trig_chan'): + if hasattr(chan, 'trig_chan') and chan.trig_chan is not None: trig_chan = chan.trig_chan if not (hasattr(seq[ct], 'pulses') and trig_chan in seq[ct].pulses.keys()): diff --git a/QGL/config.py b/QGL/config.py index c21dd560..18ca79c5 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -12,7 +12,7 @@ db_resource_name = None # The config file (executed upon channel library loading) -config_file = None +# config_file = None # plotting options plotBackground = '#EAEAF2' From 4949158eab155226bffb25fd183a0cc748e9beda Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 15 Nov 2018 17:05:09 -0500 Subject: [PATCH 070/235] Fix saving and loading and interactions with QubitFactory etc. --- QGL/ChannelLibraries.py | 80 +++++++++++++++++++++++------------------ QGL/Compiler.py | 2 +- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 338766cf..f94cc74a 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -57,9 +57,14 @@ def check_session_dirty(f): """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" @wraps(f) def wrapper(cls, *args, **kwargs): - if 'force' in kwargs and kwargs['force']: + if (len(cls.session.dirty | cls.session.new)) == 0: + if 'force' in kwargs: + kwargs.pop('force') return f(cls, *args, **kwargs) - elif len(cls.session.dirty | cls.session.new) != 0: + elif 'force' in kwargs and kwargs['force']: + kwargs.pop('force') + return f(cls, *args, **kwargs) + else: raise Exception("Uncommitted transactions for working database. Either use force=True or commit/revert your changes.") return wrapper @@ -91,7 +96,7 @@ def __init__(self, db_resource_name=None): bbndb.Base.metadata.create_all(bbndb.engine) bbndb.Session.configure(bind=bbndb.engine) self.Session = bbndb.Session - self.session = self.Session() + bbndb.session = self.session = self.Session() self.connectivityG = nx.DiGraph() @@ -103,7 +108,7 @@ def __init__(self, db_resource_name=None): self.channelDatabase = working_dbs[0] elif len(working_dbs) == 0: self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) - self.session.add(self.channelDatabase) + self.add_and_update_dict(self.channelDatabase) self.session.commit() self.update_channelDict() @@ -185,7 +190,7 @@ def clear(self, channel_db=None, create_new=True): if create_new: self.channelDatabase = Channels.ChannelDatabase(label="working", time=datetime.datetime.now()) - self.session.add(self.channelDatabase) + self.add_and_update_dict(self.channelDatabase) self.session.commit() channelLib = self @@ -193,10 +198,12 @@ def load_obj(self, obj): self.clear(create_new=False) self.channelDatabase = bbndb.deepcopy_sqla_object(obj, self.session) self.channelDatabase.label = "working" + self.session.commit() self.update_channelDict() def commit(self): self.session.commit() + self.update_channelDict() def revert(self): self.session.rollback() @@ -210,6 +217,10 @@ def save_as(self, name): new_channelDatabase.time = datetime.datetime.now() self.commit() + def add_and_update_dict(self, el): + self.session.add(el) + self.update_channelDict() + #Dictionary methods def __getitem__(self, key): return self.channelDict[key] @@ -251,7 +262,7 @@ def new_APS2(self, label, address): this_transmitter.trigger_source = "external" this_transmitter.address = address - self.session.add(this_transmitter) + self.add_and_update_dict(this_transmitter) return this_transmitter def new_APS2_rack(self, label, num, start_address): @@ -260,7 +271,7 @@ def new_APS2_rack(self, label, num, start_address): transmitters = [new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase) - self.session.add(this_transceiver) + self.add_and_update_dict(this_transceiver) return this_transceiver def new_X6(self, label, address, dsp_channel=0, record_length=1024): @@ -277,7 +288,7 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024): chan1.kernel = np.ones(record_length, dtype=np.complex).tobytes() chan2.kernel = np.ones(record_length, dtype=np.complex).tobytes() - self.session.add(this_receiver) + self.add_and_update_dict(this_receiver) return this_receiver def new_Alazar(self, label, address, record_length=1024): @@ -290,17 +301,17 @@ def new_Alazar(self, label, address, record_length=1024): this_receiver.stream_types = "raw" this_receiver.address = address - self.session.add(this_receiver) + self.add_and_update_dict(this_receiver) return this_receiver - def new_qubit(self, label): - thing = Channels.Qubit(label=label, channel_db=self.channelDatabase) - self.session.add(thing) + def new_qubit(self, label, **kwargs): + thing = Channels.Qubit(label=label, channel_db=self.channelDatabase, **kwargs) + self.add_and_update_dict(thing) return thing def new_source(self, label, model, address, power=-30.0, frequency=5.0e9): thing = Channels.Generator(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=self.channelDatabase) - self.session.add(thing) + self.add_and_update_dict(thing) return thing def set_control(self, qubit, transmitter, generator=None): @@ -344,6 +355,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan + qubit.measure_chan = meas if isinstance(receivers, Channels.Receiver) and len(receivers.channels) > 1: raise ValueError("In set_measure the Receiver must have a single receiver channel or a specific channel must be passed instead") @@ -372,35 +384,33 @@ def set_master(self, transmitter, trig_channel, pulse_length=1e-7): transmitter.master = True transmitter.trigger_source = "internal" -def QubitFactory(label, **kwargs): - ''' Return a saved qubit channel or create a new one. ''' - q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label).all() - if len(q) == 1: - return q[0] +def QubitFactory(label): + ''' Return a saved qubit channel''' + cs = [c for c in channelLib.channelDatabase.channels if c.label==label] + # q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all() + if len(cs) == 1: + return cs[0] else: - c = Channels.Qubit(label=label, channel_db=channelLib.channelDatabase, **kwargs) - channelLib.session.add(c) - return c + raise Exception(f"Expected to find a single qubit {label} but found {len(cs)} qubits with the same label instead.") -def MeasFactory(label, **kwargs): +def MeasFactory(label): ''' Return a saved measurement channel or create a new one. ''' - q = channelLib.session.query(Channels.Measurement).filter(Channels.Measurement.label==label).all() - if len(q) == 1: - return q[0] + cs = [c for c in channelLib.channelDatabase.channels if c.label==label] + # q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all() + if len(cs) == 1: + return cs[0] else: - c = Channels.Measurement(label=label, channel_db=channelLib.channelDatabase, **kwargs) - channelLib.session.add(c) - return c + raise Exception(f"Expected to find a single measurement {label} but found {len(cs)} measurements with the same label instead.") -def MarkerFactory(label, **kwargs): +def MarkerFactory(label): ''' Return a saved Marker channel or create a new one. ''' - q = channelLib.session.query(Channels.LogicalMarkerChannel).filter(Channels.LogicalMarkerChannel.label==label).all() - if len(q) == 1: - return q[0] + cs = [c for c in channelLib.channelDatabase.channels if c.label==label] + # q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all() + if len(cs) == 1: + return cs[0] else: - c = Channels.LogicalMarkerChannel(label=label, channel_db=channelLib.channelDatabase, **kwargs) - channelLib.session.add(c) - return c + raise Exception(f"Expected to find a single marker {label} but found {len(cs)} markers with the same label instead.") + def EdgeFactory(source, target): if channelLib.connectivityG.has_edge(source, target): diff --git a/QGL/Compiler.py b/QGL/Compiler.py index ec17af38..782c08c3 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -47,7 +47,7 @@ def map_logical_to_physical(wires): for logicalChan in wires.keys(): phys_chan = logicalChan.phys_chan if not phys_chan: - raise ValueError("LogicalChannel {} does not have a PhysicalChannel".format(phys_chan)) + raise ValueError("LogicalChannel {} does not have a PhysicalChannel".format(logicalChan)) if phys_chan not in physicalChannels: physicalChannels[phys_chan] = [logicalChan] else: From 1a5a8f0da3e7048d43915bc1435560c8280e7411 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 16 Nov 2018 13:15:35 -0500 Subject: [PATCH 071/235] Clear pulse cache before recompiling, otherwise DB updates fall on deaf ears --- QGL/Compiler.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 782c08c3..a21a9df0 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -323,7 +323,7 @@ def compile_to_hardware(seqs, tdm_seq (optional): compile for TDM ''' ChannelLibraries.channelLib.update_channelDict() - # clear_pulse_cache() + clear_pulse_cache() logger.debug("Compiling %d sequence(s)", len(seqs)) From 4418a7b5945be02bdaab454c3c8ae63fac49ab79 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 16 Nov 2018 13:16:16 -0500 Subject: [PATCH 072/235] Add checking for duplicates and dirty session --- QGL/ChannelLibraries.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index f94cc74a..0b8ad87b 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -68,6 +68,16 @@ def wrapper(cls, *args, **kwargs): raise Exception("Uncommitted transactions for working database. Either use force=True or commit/revert your changes.") return wrapper +def check_for_duplicates(f): + """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" + @wraps(f) + def wrapper(cls, label, *args, **kwargs): + if label in cls.channelDict: + raise ValueError(f"Cannot create {label}: a channel with the same name already exists.") + else: + return f(cls, label, *args, **kwargs) + return wrapper + class ChannelLibrary(object): def __init__(self, db_resource_name=None): @@ -208,6 +218,7 @@ def commit(self): def revert(self): self.session.rollback() + @check_session_dirty def save_as(self, name): if name == "working": raise ValueError("Cannot save as `working` since that is the default working environment name...") @@ -251,6 +262,7 @@ def build_connectivity_graph(self): self.connectivityG[chan.source][chan.target]['channel'] = chan >>>>>>> Ditch atom, move to Pony.orm for all channel library objects. + @check_for_duplicates def new_APS2(self, label, address): chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) @@ -265,6 +277,7 @@ def new_APS2(self, label, address): self.add_and_update_dict(this_transmitter) return this_transmitter + @check_for_duplicates def new_APS2_rack(self, label, num, start_address): address_start = ".".join(start_address.split(".")[:3]) address_end = int(start_address.split(".")[-1]) @@ -274,6 +287,7 @@ def new_APS2_rack(self, label, num, start_address): self.add_and_update_dict(this_transceiver) return this_transceiver + @check_for_duplicates def new_X6(self, label, address, dsp_channel=0, record_length=1024): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=self.channelDatabase) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=self.channelDatabase) @@ -291,6 +305,7 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024): self.add_and_update_dict(this_receiver) return this_receiver + @check_for_duplicates def new_Alazar(self, label, address, record_length=1024): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=self.channelDatabase) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=self.channelDatabase) @@ -304,11 +319,13 @@ def new_Alazar(self, label, address, record_length=1024): self.add_and_update_dict(this_receiver) return this_receiver + @check_for_duplicates def new_qubit(self, label, **kwargs): thing = Channels.Qubit(label=label, channel_db=self.channelDatabase, **kwargs) self.add_and_update_dict(thing) return thing + @check_for_duplicates def new_source(self, label, model, address, power=-30.0, frequency=5.0e9): thing = Channels.Generator(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=self.channelDatabase) self.add_and_update_dict(thing) From d81b5f812684f0b1e15a9a1fcb3d32cffb75849c Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 16 Nov 2018 13:16:53 -0500 Subject: [PATCH 073/235] Update channel lib automatically when loading factories --- QGL/ChannelLibraries.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 0b8ad87b..749d2056 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -403,6 +403,7 @@ def set_master(self, transmitter, trig_channel, pulse_length=1e-7): def QubitFactory(label): ''' Return a saved qubit channel''' + channelLib.update_channelDict() cs = [c for c in channelLib.channelDatabase.channels if c.label==label] # q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all() if len(cs) == 1: @@ -412,6 +413,7 @@ def QubitFactory(label): def MeasFactory(label): ''' Return a saved measurement channel or create a new one. ''' + channelLib.update_channelDict() cs = [c for c in channelLib.channelDatabase.channels if c.label==label] # q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all() if len(cs) == 1: @@ -422,14 +424,15 @@ def MeasFactory(label): def MarkerFactory(label): ''' Return a saved Marker channel or create a new one. ''' cs = [c for c in channelLib.channelDatabase.channels if c.label==label] + channelLib.update_channelDict() # q = channelLib.session.query(Channels.Qubit).filter(Channels.Qubit.label==label and Channels.Qubit.channel_db==channelLib.channelDatabase).all() if len(cs) == 1: return cs[0] else: raise Exception(f"Expected to find a single marker {label} but found {len(cs)} markers with the same label instead.") - def EdgeFactory(source, target): + channelLib.update_channelDict() if channelLib.connectivityG.has_edge(source, target): return channelLib.connectivityG[source][target]['channel'] elif channelLib.connectivityG.has_edge(target, source): From 7fa03ea219bf8b0ab2e740bb06c558a23a9045ac Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 16 Nov 2018 16:02:00 -0500 Subject: [PATCH 074/235] Pretty printing filter channel library versions --- QGL/ChannelLibraries.py | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 749d2056..fdb82599 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -50,6 +50,7 @@ from sqlalchemy.orm.session import make_transient from sqlalchemy.pool import StaticPool +from IPython.display import HTML, display channelLib = None @@ -130,7 +131,13 @@ def query(self, obj_type, **kwargs): return self.session.query(obj_type).filter_by(**kwargs) def get_current_channels(self): - return self.channelDatabase.channels + self.channelDatabase.generators + return (self.channelDatabase.channels + + self.channelDatabase.generators + + self.channelDatabase.transmitters + + self.channelDatabase.receivers + + self.channelDatabase.transceivers + + self.channelDatabase.instruments + + self.channelDatabase.processors) def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} @@ -139,9 +146,12 @@ def ls(self): cdb = Channels.ChannelDatabase q = self.session.query(cdb.label, cdb.time, cdb.id).\ order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label).all() + table_code = "" for i, (label, time, id) in enumerate(q): - t = time.strftime("(%Y) %b. %d @ %I:%M:%S %p") - print(f"[{id}] {t} -> {label}") + y, d, t = map(time.strftime, ["%Y", "%b. %d", "%I:%M:%S %p"]) + # t = time.strftime("(%Y) %b. %d @ %I:%M:%S %p") + table_code += f"{id}{y}{d}{t}{label}" + display(HTML(f"{table_code}
idYearDateTimeName
")) def ent_by_type(self, obj_type, show=False): q = self.session.query(obj_type).filter(obj_type.channel_db.has(label="working")).order_by(obj_type.label).all() From e76e1a5343e3b029a68ed78eb36daa6e245490b9 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 19 Nov 2018 10:23:58 -0500 Subject: [PATCH 075/235] Added ability to delete channel library versions --- QGL/ChannelLibraries.py | 16 ++++++++++++++-- README.md | 9 +-------- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index fdb82599..f6adb63e 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -197,8 +197,7 @@ def load(self, name, index=1): @check_session_dirty def load_by_id(self, id_num): - cdb = Channels.ChannelDatabase - item = self.session.query(cdb).filter(cdb.id==id_num).first() + item = self.session.query(Channels.ChannelDatabase).filter_by(id=id_num).first() self.load_obj(item) def clear(self, channel_db=None, create_new=True): @@ -214,6 +213,19 @@ def clear(self, channel_db=None, create_new=True): self.session.commit() channelLib = self + def rm(self, library_name, keep_id=-1): + """Remove the channel library named `library_name`. If no `keep_version` is specified then + all versions are removed. Otherwise """ + cdb = Channels.ChannelDatabase + items = self.session.query(cdb).filter(cdb.label==library_name and cdb.id!=keep_id).all() + for item in items: + self.session.delete(item) + + def rm_by_id(self, id): + """Remove the channel library with id `id`""" + item = self.session.query(Channels.ChannelDatabase).filter_by(id=id_num).first() + self.session.delete(item) + def load_obj(self, obj): self.clear(create_new=False) self.channelDatabase = bbndb.deepcopy_sqla_object(obj, self.session) diff --git a/README.md b/README.md index 57b51930..974fdbdd 100644 --- a/README.md +++ b/README.md @@ -54,16 +54,9 @@ The QGL config file will be created the first time you run `import QGL` or `from ## Dependencies * Python 2.7 or 3.4+ -* JSONLibraryUtils (https://github.com/BBN-Q/JSONLibraryUtils, integrated as a Git submodule) * Numpy/Scipy -* Nucleic atom (from ecpy channel for Python 3) * h5py -* watchdog * Bokeh 0.11 * networkx 2.0 * iPython/Jupyter 4.0 (only for Jupyter notebooks) -* ruamel_yaml - -## UnitTest data support -This repository uses the Git Large File Storage (LFS) extension to manage a few -UnitTest data files (see https://git-lfs.github.com/). +* bbndb From be6e5385947285922faf37f47d84ff02a130ee3c Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 7 Dec 2018 09:40:23 -0500 Subject: [PATCH 076/235] Fix timing for connectivity building --- QGL/ChannelLibraries.py | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index f6adb63e..9b18ea3c 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -141,6 +141,7 @@ def get_current_channels(self): def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} + self.build_connectivity_graph() def ls(self): cdb = Channels.ChannelDatabase @@ -251,7 +252,10 @@ def save_as(self, name): self.commit() def add_and_update_dict(self, el): - self.session.add(el) + if isinstance(el, list): + self.session.add_all(el) + else: + self.session.add(el) self.update_channelDict() #Dictionary methods @@ -303,7 +307,7 @@ def new_APS2(self, label, address): def new_APS2_rack(self, label, num, start_address): address_start = ".".join(start_address.split(".")[:3]) address_end = int(start_address.split(".")[-1]) - transmitters = [new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] + transmitters = [self.new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase) self.add_and_update_dict(this_transceiver) @@ -370,6 +374,14 @@ def set_control(self, qubit, transmitter, generator=None): if generator: qubit.phys_chan.generator = generator + def set_qubit_connectivity(self, graph): + """ + Graph is a networkx DiGraph consisting of edges (source qubit, target qubit) + """ + new_edges = [Channels.Edge(label=f"{source.label}->{target.label}", source=source, target=target) for source, target in graph.edges()] + self.add_and_update_dict(new_edges) + return new_edges + def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] From 61c4cf31cdd32af49b46d401c0f1e1248514e93d Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 11 Dec 2018 11:09:41 -0500 Subject: [PATCH 077/235] Now properly adding all new objects to the session. --- QGL/ChannelLibraries.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 9b18ea3c..abe7168f 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -373,6 +373,7 @@ def set_control(self, qubit, transmitter, generator=None): qubit.phys_chan = phys_chan if generator: qubit.phys_chan.generator = generator + self.update_channelDict() def set_qubit_connectivity(self, graph): """ @@ -418,12 +419,14 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") meas.receiver_chan = rcv_chan + self.add_and_update_dict([meas, trig_chan]) if gate: phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("12m2") gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=self.channelDatabase) gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan + self.add_and_update_dict([gate_chan]) def set_master(self, transmitter, trig_channel, pulse_length=1e-7): if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): @@ -434,6 +437,7 @@ def set_master(self, transmitter, trig_channel, pulse_length=1e-7): st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} transmitter.master = True transmitter.trigger_source = "internal" + self.add_and_update_dict([st]) def QubitFactory(label): ''' Return a saved qubit channel''' From 5fd7a903caec83ef615c2ca14f3e31573af7277c Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 19 Dec 2018 16:39:49 -0500 Subject: [PATCH 078/235] Replace historical channel naming convention ch12-> ch1, ch12m2->m2 etc. --- QGL/ChannelLibraries.py | 16 ++++++++-------- QGL/Compiler.py | 3 ++- QGL/drivers/APS2Pattern.py | 22 +++++++++++----------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index abe7168f..90306e98 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -290,13 +290,13 @@ def build_connectivity_graph(self): @check_for_duplicates def new_APS2(self, label, address): - chan12 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m1 = Channels.PhysicalMarkerChannel(label=f"{label}-12m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m2 = Channels.PhysicalMarkerChannel(label=f"{label}-12m2", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m3 = Channels.PhysicalMarkerChannel(label=f"{label}-12m3", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m4 = Channels.PhysicalMarkerChannel(label=f"{label}-12m4", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-m2", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-m3", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-m4", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan12, m1, m2, m3, m4], channel_db=self.channelDatabase) + this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan1, m1, m2, m3, m4], channel_db=self.channelDatabase) this_transmitter.trigger_source = "external" this_transmitter.address = address @@ -401,7 +401,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe if generator: meas.phys_chan.generator = generator - phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("12m1") + phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("m1") trig_chan = Channels.LogicalMarkerChannel(label=f"receiversTrig-{qubit.label}", channel_db=self.channelDatabase) trig_chan.phys_chan = phys_trig_channel @@ -422,7 +422,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe self.add_and_update_dict([meas, trig_chan]) if gate: - phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("12m2") + phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("m2") gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=self.channelDatabase) gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan diff --git a/QGL/Compiler.py b/QGL/Compiler.py index a21a9df0..115338de 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -276,7 +276,8 @@ def bundle_wires(physWires, wfs): awgData = setup_awg_channels(physWires.keys()) for chan in physWires.keys(): _, awgChan = chan.label.rsplit('-', 1) - awgChan = 'ch' + awgChan + if awgChan[0] != 'm': + awgChan = 'ch' + awgChan awgData[chan.instrument][awgChan]['linkList'] = physWires[chan] awgData[chan.instrument][awgChan]['wfLib'] = wfs[chan] if hasattr(chan, 'correctionT'): diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 1f174564..4291e34d 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -106,7 +106,7 @@ SEQFILE_PER_CHANNEL = False def get_empty_channel_set(): - return {'ch12': {}, 'ch12m1': {}, 'ch12m2': {}, 'ch12m3': {}, 'ch12m4': {}} + return {'ch1': {}, 'm1': {}, 'm2': {}, 'm3': {}, 'm4': {}} def get_seq_file_extension(): @@ -1024,11 +1024,11 @@ def write_sequence_file(awgData, fileName): Main function to pack channel sequences into an APS2 h5 file. ''' # Convert QGL IR into a representation that is closer to the hardware. - awgData['ch12']['linkList'], wfLib = preprocess( - awgData['ch12']['linkList'], awgData['ch12']['wfLib']) + awgData['ch1']['linkList'], wfLib = preprocess( + awgData['ch1']['linkList'], awgData['ch1']['wfLib']) # compress marker data - for field in ['ch12m1', 'ch12m2', 'ch12m3', 'ch12m4']: + for field in ['m1', 'm2', 'm3', 'm4']: if 'linkList' in awgData[field].keys(): PatternUtils.convert_lengths_to_samples(awgData[field]['linkList'], SAMPLING_RATE, 1, @@ -1041,10 +1041,10 @@ def write_sequence_file(awgData, fileName): wfInfo = [] wfInfo.append(create_wf_vector({key: wf.real for key, wf in wfLib.items()}, awgData[ - 'ch12']['linkList'])) + 'ch1']['linkList'])) wfInfo.append(create_wf_vector({key: wf.imag for key, wf in wfLib.items()}, awgData[ - 'ch12']['linkList'])) + 'ch1']['linkList'])) if SAVE_WF_OFFSETS: #create a set of all waveform signatures in offset dictionaries @@ -1055,7 +1055,7 @@ def write_sequence_file(awgData, fileName): wf_sigs |= set(offset_dict.keys()) #create dictionary linking entry labels (that's what we'll have later) with offsets offsets = {} - for seq in awgData['ch12']['linkList']: + for seq in awgData['ch1']['linkList']: for entry in seq: if len(wf_sigs) == 0: break @@ -1079,7 +1079,7 @@ def write_sequence_file(awgData, fileName): # build instruction vector seq_data = [awgData[s]['linkList'] - for s in ['ch12', 'ch12m1', 'ch12m2', 'ch12m3', 'ch12m4']] + for s in ['ch1', 'm1', 'm2', 'm3', 'm4']] instructions = create_instr_data(seq_data, wfInfo[0][1], wfInfo[0][2]) #Open the HDF5 file @@ -1114,10 +1114,10 @@ def write_sequence_file(awgData, fileName): def read_sequence_file(fileName): """ Reads a HDF5 sequence file and returns a dictionary of lists. - Dictionary keys are channel strings such as ch1, ch12m1 + Dictionary keys are channel strings such as ch1, m1 Lists are or tuples of time-amplitude pairs (time, output) """ - chanStrs = ['ch1', 'ch2', 'ch12m1', 'ch12m2', 'ch12m3', 'ch12m4', + chanStrs = ['ch1', 'ch2', 'm1', 'm2', 'm3', 'm4', 'mod_phase'] seqs = {ch: [] for ch in chanStrs} @@ -1189,7 +1189,7 @@ def start_new_seq(): seqs[chan][-1].append((1, sample)) elif instr.opcode == MARKER: - chan = 'ch12m' + str(((instr.header >> 2) & 0x3) + 1) + chan = 'm' + str(((instr.header >> 2) & 0x3) + 1) count = instr.payload & 0xffffffff count = (count + 1) * ADDRESS_UNIT state = (instr.payload >> 32) & 0x1 From 72582af642d091ad4c17dd8733eaabd962dcaef6 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Fri, 25 Jan 2019 14:34:01 -0500 Subject: [PATCH 079/235] X6 and TDM compatibility. -- GER, GJR --- QGL/ChannelLibraries.py | 97 ++++++++++++++++++++++++++----------- QGL/PulseSequencePlotter.py | 12 ++--- 2 files changed, 74 insertions(+), 35 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 90306e98..f7aecd7e 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -38,6 +38,7 @@ import importlib import inspect from functools import wraps +import itertools import numpy as np import networkx as nx @@ -76,7 +77,7 @@ def wrapper(cls, label, *args, **kwargs): if label in cls.channelDict: raise ValueError(f"Cannot create {label}: a channel with the same name already exists.") else: - return f(cls, label, *args, **kwargs) + return f(cls, label, *args, **kwargs) return wrapper class ChannelLibrary(object): @@ -131,12 +132,12 @@ def query(self, obj_type, **kwargs): return self.session.query(obj_type).filter_by(**kwargs) def get_current_channels(self): - return (self.channelDatabase.channels + - self.channelDatabase.generators + - self.channelDatabase.transmitters + - self.channelDatabase.receivers + - self.channelDatabase.transceivers + - self.channelDatabase.instruments + + return (self.channelDatabase.channels + + self.channelDatabase.generators + + self.channelDatabase.transmitters + + self.channelDatabase.receivers + + self.channelDatabase.transceivers + + self.channelDatabase.instruments + self.channelDatabase.processors) def update_channelDict(self): @@ -147,7 +148,7 @@ def ls(self): cdb = Channels.ChannelDatabase q = self.session.query(cdb.label, cdb.time, cdb.id).\ order_by(Channels.ChannelDatabase.id, Channels.ChannelDatabase.label).all() - table_code = "" + table_code = "" for i, (label, time, id) in enumerate(q): y, d, t = map(time.strftime, ["%Y", "%b. %d", "%I:%M:%S %p"]) # t = time.strftime("(%Y) %b. %d @ %I:%M:%S %p") @@ -304,29 +305,52 @@ def new_APS2(self, label, address): return this_transmitter @check_for_duplicates - def new_APS2_rack(self, label, num, start_address): - address_start = ".".join(start_address.split(".")[:3]) - address_end = int(start_address.split(".")[-1]) - transmitters = [self.new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] + def new_TDM(self, label, address): + return Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6) + + @check_for_duplicates + def new_APS2_rack(self, label, num, start_address, tdm_ip=None): + address_start = ".".join(start_address.split(".")[:3]) + address_end = int(start_address.split(".")[-1]) + transmitters = [self.new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] + this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase) + for t in transmitters: + t.transceiver = this_transceiver + + if tdm_ip: + tdm = self.new_TDM(f"{label}_TDM", tdm_ip) + this_transceiver.processors = [tdm] + for t in transmitters: + t.trigger_source = 'system' self.add_and_update_dict(this_transceiver) return this_transceiver @check_for_duplicates def new_X6(self, label, address, dsp_channel=0, record_length=1024): - chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, dsp_channel=dsp_channel, channel_db=self.channelDatabase) - chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, dsp_channel=dsp_channel, channel_db=self.channelDatabase) - this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=[chan1, chan2], + phys_channels = (1, 2) + dsp_channels = (1, 2) + stream_types = ("raw", "demodulated", "integrated") + + chans = [] + + for p, d, s in itertools.product(phys_channels, dsp_channels, stream_types): + chans.append(Channels.ReceiverChannel(label=f"RecvChan-{label}-{s}-{d}-{p}", + channel=p, dsp_channel=d, stream_type=s, + channel_db=self.channelDatabase)) + + this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=chans, record_length=record_length, channel_db=self.channelDatabase) this_receiver.trigger_source = "external" this_receiver.stream_types = "raw, demodulated, integrated" this_receiver.address = address # Add a default kernel - chan1.kernel = np.ones(record_length, dtype=np.complex).tobytes() - chan2.kernel = np.ones(record_length, dtype=np.complex).tobytes() + for chan in chans: + if chan.stream_type is "integrated": + chan.kernel = np.ones(record_length, dtype=np.complex).tobytes() self.add_and_update_dict(this_receiver) return this_receiver @@ -352,8 +376,10 @@ def new_qubit(self, label, **kwargs): return thing @check_for_duplicates - def new_source(self, label, model, address, power=-30.0, frequency=5.0e9): - thing = Channels.Generator(label=label, model=model, address=address, power=power, frequency=frequency, channel_db=self.channelDatabase) + def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, reference=None): + thing = Channels.Generator(label=label, model=model, address=address, power=power, + frequency=frequency, reference=reference, + channel_db=self.channelDatabase) self.add_and_update_dict(thing) return thing @@ -396,6 +422,8 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe else: raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + if f"M-{qubit.label}" in self.channelDict: + raise ValueError(f"Cannot create Measurement M-{qubit.label}: a channel with the same name already exists.") meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=self.channelDatabase) meas.phys_chan = phys_chan if generator: @@ -403,7 +431,9 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("m1") - trig_chan = Channels.LogicalMarkerChannel(label=f"receiversTrig-{qubit.label}", channel_db=self.channelDatabase) + trig_chan = Channels.LogicalMarkerChannel(label=f"ReceiverTrig-{qubit.label}", channel_db=self.channelDatabase) + # print(phys_trig_channel.id, trig_chan.id) + self.session.add(trig_chan) trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan @@ -428,16 +458,25 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe meas.gate_chan = gate_chan self.add_and_update_dict([gate_chan]) - def set_master(self, transmitter, trig_channel, pulse_length=1e-7): - if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): - raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") + def set_master(self, master_instrument, trig_channel=None, pulse_length=1e-7): + + if isinstance(master_instrument, Channels.Processor): + master_instrument.master = True + + elif trig_channel: - st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=self.channelDatabase) - st.phys_chan = trig_channel - st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} - transmitter.master = True - transmitter.trigger_source = "internal" - self.add_and_update_dict([st]) + if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): + raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") + + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=self.channelDatabase) + st.phys_chan = trig_channel + st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} + master_instrument.master = True + master_instrument.trigger_source = "internal" + self.add_and_update_dict([st]) + + else: + raise ValueError(f"Could not determine which transmitter to set as master for {transmitter}:{trig_channel}") def QubitFactory(label): ''' Return a saved qubit channel''' diff --git a/QGL/PulseSequencePlotter.py b/QGL/PulseSequencePlotter.py index e2b14ed5..db41886e 100644 --- a/QGL/PulseSequencePlotter.py +++ b/QGL/PulseSequencePlotter.py @@ -78,7 +78,7 @@ def plot_pulse_files(metafile, time=False): Helper function to plot a list of AWG files. A JS slider allows choice of sequence number. ''' #If we only go one filename turn it into a list - + with open(metafile, 'r') as FID: meta_info = json.load(FID) fileNames = [] @@ -166,8 +166,8 @@ def extract_waveforms(fileNames, nameDecorator='', time=False): # Assume a naming convention path/to/file/SequenceName-AWGName.h5 AWGName = "-".join((os.path.split(os.path.splitext(fileName)[0])[1]).split('-')[1:]) # Strip any _ suffix - if '_' in AWGName: - AWGName = AWGName[:AWGName.index('_')] + # if '_' in AWGName: + # AWGName = AWGName[:AWGName.index('_')] translator = resolve_translator(fileName, translators) wfs = translator.read_sequence_file(fileName) @@ -177,7 +177,7 @@ def extract_waveforms(fileNames, nameDecorator='', time=False): if all_zero_seqs(seqs): continue num_seqs = max(num_seqs, len(seqs)) - line_names.append((AWGName + nameDecorator + '_' + k).replace("-","_")) + line_names.append((AWGName + nameDecorator + '_' + k).replace("-","_")) k_ = line_names[-1].replace("-", "_") for ct, seq in enumerate(seqs): data_dicts[k_ + "_{:d}".format(ct + 1)] = {} @@ -205,7 +205,7 @@ def plot_pulse_files_compare(metafile1, metafile2, time=False): with open(metafile1, 'r') as FID: meta_info1 = json.load(FID) - + for el in meta_info1["instruments"].values(): # Accomodate seq_file per instrument and per channel if isinstance(el, str): @@ -216,7 +216,7 @@ def plot_pulse_files_compare(metafile1, metafile2, time=False): with open(metafile2, 'r') as FID: meta_info2 = json.load(FID) - + for el in meta_info2["instruments"].values(): # Accomodate seq_file per instrument and per channel if isinstance(el, str): From 93654eac223095bfc9b6c73a6dc7141d3b95c35c Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Fri, 25 Jan 2019 15:59:31 -0500 Subject: [PATCH 080/235] Change APS2 rack setup --- QGL/ChannelLibraries.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index f7aecd7e..4499e48c 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -309,11 +309,8 @@ def new_TDM(self, label, address): return Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6) @check_for_duplicates - def new_APS2_rack(self, label, num, start_address, tdm_ip=None): - address_start = ".".join(start_address.split(".")[:3]) - address_end = int(start_address.split(".")[-1]) - transmitters = [self.new_APS2(f"{label}_U{i}", f"{address_start}.{address_end+i}") for i in range(1,num+1)] - + def new_APS2_rack(self, label, ip_addresses, tdm_ip=None): + transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase) for t in transmitters: t.transceiver = this_transceiver From fb89dcac63b420a81abb7153ecb85b0bc3897608 Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Fri, 25 Jan 2019 16:24:11 -0500 Subject: [PATCH 081/235] Properly set edges. --- QGL/ChannelLibraries.py | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 4499e48c..79331c1a 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -380,7 +380,7 @@ def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, refere self.add_and_update_dict(thing) return thing - def set_control(self, qubit, transmitter, generator=None): + def set_control(self, qubit_or_edge, transmitter, generator=None): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] @@ -393,11 +393,19 @@ def set_control(self, qubit, transmitter, generator=None): else: raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - qubit.phys_chan = phys_chan + qubit_or_edge.phys_chan = phys_chan if generator: - qubit.phys_chan.generator = generator + qubit_or_edge.phys_chan.generator = generator self.update_channelDict() + def new_edge(self, source, target): + label = f"{source.label}->{target.label}" + if label in self.channelDict: + raise ValueError("Cannot construct edge {label} since it is already in the channel library.") + edge = Channels.Edge(label=f"{source.label}->{target.label}", source=source, target=target, channel_db=self.channelDatabase) + self.add_and_update_dict(edge) + return edge + def set_qubit_connectivity(self, graph): """ Graph is a networkx DiGraph consisting of edges (source qubit, target qubit) From 88e9bdf278fd5b5f000753abadd19a5884cd66b0 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 28 Jan 2019 16:52:30 -0500 Subject: [PATCH 082/235] Remove merge conflict detritus --- QGL/ChannelLibraries.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 79331c1a..64588967 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -280,14 +280,11 @@ def values(self): def build_connectivity_graph(self): # build connectivity graph - for chan in select(q for q in Channels.Qubit if q not in self.connectivityG): -======= for chan in self.session.query(Channels.Qubit).filter(Channels.Qubit not in self.connectivityG).all(): self.connectivityG.add_node(chan) for chan in self.session.query(Channels.Edge): #select(e for e in Channels.Edge): self.connectivityG.add_edge(chan.source, chan.target) self.connectivityG[chan.source][chan.target]['channel'] = chan ->>>>>>> Ditch atom, move to Pony.orm for all channel library objects. @check_for_duplicates def new_APS2(self, label, address): From be6e8a6c44109b6e621aa74fac0e4461436f21ac Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 29 Jan 2019 16:21:14 -0500 Subject: [PATCH 083/235] Fix up travis yml from merge --- .travis.yml | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/.travis.yml b/.travis.yml index d936dc77..4258bc7e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,6 +4,9 @@ language: python python: - 3.6 +env: + - CONDA_TYPE=miniconda CONDA_VERS=https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh + - CONDA_TYPE=miniconda CONDA_VERS=https://repo.continuum.io/miniconda/Miniconda3-4.5.11-Linux-x86_64.sh before_install: # install git lfs and fetch test data @@ -22,11 +25,11 @@ install: # We do this conditionally because it saves us some downloading if the # version is the same. - - wget https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -O miniconda.sh; + - echo "Using $CONDA_TYPE" + - wget $CONDA_VERS -O miniconda.sh + - bash miniconda.sh -h | sed '4q;d' - bash miniconda.sh -b -p $HOME/miniconda - export PATH="$HOME/miniconda/bin:$PATH" - - export BBN_MEAS_FILE="$PWD/tests/test_measure.yml" - - echo "Measure file at $BBN_MEAS_FILE" - hash -r - conda config --set always_yes yes --set changeps1 no - conda update -q conda @@ -34,10 +37,9 @@ install: - conda info -a # Create conda environment with dependencies - - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy bokeh h5py jupyter scipy networkx future + - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy bokeh h5py jupyter scipy networkx - source activate test-environment - - conda install -c ecpy atom; - - pip install watchdog coveralls + - pip install coveralls - if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install pygsti; fi script: From 3e14ce85605af80f0c4945f0c67b27a142825d79 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Tue, 5 Feb 2019 16:47:54 -0500 Subject: [PATCH 084/235] New spec an --- QGL/ChannelLibraries.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 64588967..8515aa5d 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -305,6 +305,10 @@ def new_APS2(self, label, address): def new_TDM(self, label, address): return Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6) + @check_for_duplicates + def new_spectrum_analzyer(self, label, address, source): + return Channels.SpectrumAnalyzer(label=label, model="SpectrumAnalyzer", address=address, LO_source=source) + @check_for_duplicates def new_APS2_rack(self, label, ip_addresses, tdm_ip=None): transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] From 1bfe23578d5f28fb7abb45929d41556153d5171b Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 8 Feb 2019 13:39:14 -0500 Subject: [PATCH 085/235] Pass frequency keyword to enable baked in frequency sweeps --- QGL/PulsePrimitives.py | 6 +++++- QGL/PulseSequencer.py | 6 ++++-- QGL/drivers/APS2Pattern.py | 2 +- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 59d48099..bfbf03b2 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -91,6 +91,10 @@ def Utheta(qubit, del params["amp"] if "phase" in params: del params["phase"] + if "frequency" in kwargs: + frequency = kwargs["frequency"] + else: + frequency = None # allow override of angle -> amplitude lookup if the user provides an "amp" # keyword argument if "amp" in kwargs: @@ -109,7 +113,7 @@ def Utheta(qubit, else: # linearly scale based upon the 'pi/2' amplitude amp = (angle / (pi/2)) * qubit.pulse_params['pi2Amp'] - return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams) + return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency) # generic pulses around X, Y, and Z axes diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 34d1f07f..315c3db8 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -36,8 +36,10 @@ class Pulse(namedtuple("Pulse", ["label", "channel", "length", "amp", "phase", " "maddr", "moffset"])): __slots__ = () - def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, ignoredStrParams=[], maddr=-1, moffset=0): - if hasattr(channel, 'frequency'): + def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, ignoredStrParams=[], maddr=-1, moffset=0, frequency=None): + if frequency: + frequency = frequency + elif hasattr(channel, 'frequency'): frequency = channel.frequency else: frequency = 0 diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 4291e34d..60bb0657 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -163,7 +163,7 @@ def create_wf_vector(wfLib, seqs): else: #otherwise fill in one cache line at a time - CACHE_LINE_LENGTH = WAVEFORM_CACHE_SIZE / 2 + CACHE_LINE_LENGTH = int(np.round(WAVEFORM_CACHE_SIZE / 2)) - 1 wfVec = np.zeros(CACHE_LINE_LENGTH, dtype=np.int16) offsets = [{}] cache_lines = [] From a7c7ef305c7d2570739ea95c1627e0d08a7a83f1 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 7 Feb 2019 17:07:43 -0500 Subject: [PATCH 086/235] Replace H5 with simple binary format in aps2 --- QGL/drivers/APS2Pattern.py | 68 +++++++++++++++++++------------------- 1 file changed, 34 insertions(+), 34 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 60bb0657..38986a2b 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -23,7 +23,7 @@ from future.moves.itertools import zip_longest import pickle -import h5py +import struct import numpy as np from QGL import Compiler, ControlFlow, BlockLabel, PatternUtils @@ -108,17 +108,13 @@ def get_empty_channel_set(): return {'ch1': {}, 'm1': {}, 'm2': {}, 'm3': {}, 'm4': {}} - def get_seq_file_extension(): - return '.h5' - + return '.aps2' def is_compatible_file(filename): - with h5py.File(filename, 'r') as FID: - target = FID['/'].attrs['target hardware'] - if isinstance(target, str): - target = target.encode('utf-8') - if target == b'APS2': + with open(filename, 'rb') as FID: + byte = FID.read(4) + if byte == b'APS2': return True return False @@ -1082,34 +1078,33 @@ def write_sequence_file(awgData, fileName): for s in ['ch1', 'm1', 'm2', 'm3', 'm4']] instructions = create_instr_data(seq_data, wfInfo[0][1], wfInfo[0][2]) - #Open the HDF5 file + #Open the binary file if os.path.isfile(fileName): os.remove(fileName) - with h5py.File(fileName, 'w') as FID: - FID['/'].attrs['Version'] = 4.0 - FID['/'].attrs['target hardware'] = 'APS2' - FID['/'].attrs['minimum firmware version'] = 4.0 - FID['/'].attrs['channelDataFor'] = np.uint16([1, 2]) + + with open(fileName, 'wb') as FID: + FID.write(b'APS2') # target hardware + FID.write(np.float32(4.0).tobytes()) # Version + FID.write(np.float32(4.0).tobytes()) # minimum firmware version + FID.write(np.uint16(2).tobytes()) # number of channels + # FID.write(np.uint16([1, 2]).tobytes()) # channelDataFor + FID.write(np.uint64(instructions.size).tobytes()) # instructions length + FID.write(instructions.tobytes()) # instructions in uint64 form #Create the groups and datasets for chanct in range(2): - chanStr = '/chan_{0}'.format(chanct + 1) - chanGroup = FID.create_group(chanStr) + # chanStr = '/chan_{0}'.format(chanct + 1) + # chanGroup = FID.create_group(chanStr) #Write the waveformLib to file if wfInfo[chanct][0].size == 0: #If there are no waveforms, ensure that there is some element #so that the waveform group gets written to file. #TODO: Fix this in libaps2 - data = np.array([0], dtype=np.uint16) + data = np.array([0], dtype=np.int16) else: data = wfInfo[chanct][0] - FID.create_dataset(chanStr + '/waveforms', data=data) - - #Write the instructions to channel 1 - if np.mod(chanct, 2) == 0: - FID.create_dataset(chanStr + '/instructions', - data=instructions) - + FID.write(np.uint64(data.size).tobytes()) # waveform data length for channel + FID.write(data.tobytes()) def read_sequence_file(fileName): """ @@ -1130,16 +1125,21 @@ def start_new_seq(): #marker channel seqs[ch].append([]) - with h5py.File(fileName, 'r') as FID: - file_version = FID["/"].attrs["Version"] + with open(fileName, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + file_version = struct.unpack(' Date: Fri, 8 Feb 2019 12:54:17 -0500 Subject: [PATCH 087/235] Add a nice PyQt5 GUI for showing disassmbled APS2 instructions --- QGL/drivers/APS2Pattern.py | 101 ++++++++++++++++++++++++++++++------- requirements.txt | 1 - setup.py | 1 - 3 files changed, 83 insertions(+), 20 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 38986a2b..1d7bb765 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -24,6 +24,7 @@ import pickle import struct +import sys import numpy as np from QGL import Compiler, ControlFlow, BlockLabel, PatternUtils @@ -258,9 +259,9 @@ def __str__(self): wfOpCode = (self.payload >> 46) & 0x3 wfOpCodes = ["PLAY", "TRIG", "SYNC", "PREFETCH"] out += wfOpCodes[wfOpCode] - out += "; TA bit={}".format((self.payload >> 45) & 0x1) - out += ", count = {}".format((self.payload >> 24) & 2**21 - 1) - out += ", addr = {}".format(self.payload & 2**24 - 1) + out += "; TA_bit={}".format((self.payload >> 45) & 0x1) + out += ", count={}".format((self.payload >> 24) & 2**21 - 1) + out += ", addr={}".format(self.payload & 2**24 - 1) # # APS3/TDM modifier to use VRAM output # if self.payload & (1 << 48): @@ -271,7 +272,7 @@ def __str__(self): mrkOpCodes = ["PLAY", "TRIG", "SYNC"] out += mrkOpCodes[mrkOpCode] out += "; state={}".format((self.payload >> 32) & 0x1) - out += ", count = {}".format(self.payload & 2**32 - 1) + out += ", count={}".format(self.payload & 2**32 - 1) elif instrOpCode == MODULATION: modulatorOpCode = (self.payload >> 45) & 0x7 @@ -293,21 +294,21 @@ def __str__(self): cmpCodes = ["EQUAL", "NOTEQUAL", "GREATERTHAN", "LESSTHAN"] cmpCode = (self.payload >> 8) & 0x3 out += " | " + cmpCodes[cmpCode] - out += ", value = {}".format(self.payload & 0xff) + out += ", value={}".format(self.payload & 0xff) elif any( [instrOpCode == op for op in [GOTO, CALL, RET, REPEAT, PREFETCH]]): - out += " | target addr = {}".format(self.payload & 2**26 - 1) + out += " | target_addr={}".format(self.payload & 2**26 - 1) elif instrOpCode == LOAD: - out += " | count = {}".format(self.payload) + out += " | count={}".format(self.payload) elif instrOpCode == CUSTOM: store_addr = self.payload & 0xFFFF load_addr = (self.payload >> 16) & 0xFFFF instruction = (self.payload >> 32) & 0xFF instructionAPS = TDM_CUSTOM_DECODE[instruction] - out += " | instruction = {0} ({1}), load_addr = 0x{2:0x}, store_addr = 0x{3:0x}".format(instruction, instructionAPS, load_addr, store_addr) + out += " | instruction={0} ({1}), load_addr=0x{2:0x}, store_addr=0x{3:0x}".format(instruction, instructionAPS, load_addr, store_addr) elif instrOpCode == WRITEADDR: addr = self.payload & 0xFFFF @@ -333,7 +334,7 @@ def __str__(self): addr = (self.payload >> 16) & 0xFFFF value = (self.payload >> 32) & 0xFFFF - out += "{0} | addr = 0x{1:0x}, {2} = 0x{3:0x}".format(instrStr, addr, valueType, value) + out += "{0} | addr=0x{1:0x}, {2}=0x{3:0x}".format(instrStr, addr, valueType, value) elif instrOpCode == LOADCMP: addr = self.payload & 0xFFFF @@ -345,7 +346,7 @@ def __str__(self): src = "EXT" if use_ram: src = "RAM" - out += "LOADCMP | source = {0}, addr = 0x{1:0x}, read_mask = 0x{2:0x}".format(src, addr, mask) + out += "LOADCMP | source={0}, addr=0x{1:0x}, read_mask=0x{2:0x}".format(src, addr, mask) return out def __eq__(self, other): @@ -1447,14 +1448,16 @@ def write_tdm_seq(seq, tdm_fileName): # Utility Functions for displaying programs -def get_channel_instructions_string(channel): - return '/chan_{}/instructions'.format(channel) - -def raw_instructions(filename, channel = 1): - channelStr = get_channel_instructions_string(channel) - with h5py.File(filename, 'r') as fid: - raw_instrs = fid[channelStr].value.flatten() - return raw_instrs +def raw_instructions(fileName): + with open(fileName, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + file_version = struct.unpack('= 0.1 numpy >= 1.11.1 scipy >= 0.17.1 networkx >= 1.11 -h5py >= 2.6.0 bokeh >= 0.12.13 diff --git a/setup.py b/setup.py index c4db92c2..a5ef0be3 100644 --- a/setup.py +++ b/setup.py @@ -9,7 +9,6 @@ "numpy >= 1.11.1", "scipy >= 0.17.1", "networkx >= 1.11", - "h5py >= 2.6.0", "bokeh >= 0.12.13", # "pony >= 0.7.4", # This needs to be 0.7.4-dev ]) From 492a21cd65573c9cce6a1be70710d680dd42ad83 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 8 Feb 2019 16:17:49 -0500 Subject: [PATCH 088/235] No more bokeh --- QGL/Plotting.py | 75 ++++++++++++----------------- QGL/PulseSequencePlotter.py | 94 ++++++++++--------------------------- QGL/__init__.py | 2 +- QGL/drivers/APS2Pattern.py | 27 +++++------ requirements.txt | 1 - 5 files changed, 67 insertions(+), 132 deletions(-) diff --git a/QGL/Plotting.py b/QGL/Plotting.py index 25c3c1a5..254c1568 100644 --- a/QGL/Plotting.py +++ b/QGL/Plotting.py @@ -22,28 +22,13 @@ ''' import os.path, uuid, tempfile -import bokeh.plotting as bk -from bokeh.layouts import column -from bokeh.util.warnings import BokehUserWarning -from bokeh.resources import INLINE import numpy as np import warnings from . import config -def output_notebook(local=True, suppress_warnings=False): - if suppress_warnings: - warnings.simplefilter("ignore", BokehUserWarning) - if local: - bk.output_notebook(resources=INLINE) - else: - bk.output_notebook() - -def output_file(local=True, suppress_warnings=True): - if suppress_warnings: - warnings.simplefilter("ignore", BokehUserWarning) - mode = "inline" if local else "cdn" - bk.output_file(os.path.join(tempfile.gettempdir(), str(uuid.uuid4()) + - ".html"), mode=mode) +def output_notebook(): + import matplotlib + matplotlib.use("nbagg") def build_waveforms(seq): # import here to avoid circular imports @@ -76,30 +61,30 @@ def build_waveforms(seq): concatShapes[q] = np.append(concatShapes[q], 0) return concatShapes - -def plot_waveforms(waveforms, figTitle=''): - channels = waveforms.keys() - # plot - plots = [] - for (ct, chan) in enumerate(channels): - fig = bk.figure(title=figTitle + repr(chan), - plot_width=800, - plot_height=350, - y_range=[-1.05, 1.05], - x_axis_label=u'Time (μs)') - fig.background_fill_color = config.plotBackground - if config.gridColor: - fig.xgrid.grid_line_color = config.gridColor - fig.ygrid.grid_line_color = config.gridColor - waveformToPlot = waveforms[chan] - xpts = np.linspace(0, len(waveformToPlot) / chan.phys_chan.sampling_rate - / 1e-6, len(waveformToPlot)) - fig.line(xpts, np.real(waveformToPlot), color='red') - fig.line(xpts, np.imag(waveformToPlot), color='blue') - plots.append(fig) - bk.show(column(*plots)) - - -def show(seq): - waveforms = build_waveforms(seq) - plot_waveforms(waveforms) +# +# def plot_waveforms(waveforms, figTitle=''): +# channels = waveforms.keys() +# # plot +# plots = [] +# for (ct, chan) in enumerate(channels): +# fig = bk.figure(title=figTitle + repr(chan), +# plot_width=800, +# plot_height=350, +# y_range=[-1.05, 1.05], +# x_axis_label=u'Time (μs)') +# fig.background_fill_color = config.plotBackground +# if config.gridColor: +# fig.xgrid.grid_line_color = config.gridColor +# fig.ygrid.grid_line_color = config.gridColor +# waveformToPlot = waveforms[chan] +# xpts = np.linspace(0, len(waveformToPlot) / chan.phys_chan.sampling_rate +# / 1e-6, len(waveformToPlot)) +# fig.line(xpts, np.real(waveformToPlot), color='red') +# fig.line(xpts, np.imag(waveformToPlot), color='blue') +# plots.append(fig) +# bk.show(column(*plots)) + +# +# def show(seq): +# waveforms = build_waveforms(seq) +# plot_waveforms(waveforms) diff --git a/QGL/PulseSequencePlotter.py b/QGL/PulseSequencePlotter.py index db41886e..b570dc09 100644 --- a/QGL/PulseSequencePlotter.py +++ b/QGL/PulseSequencePlotter.py @@ -25,12 +25,9 @@ import os.path import json from importlib import import_module -from bokeh.layouts import column -from bokeh.models import CustomJS, ColumnDataSource, Slider -from bokeh.plotting import Figure, show -from bokeh.palettes import d3, brewer, Inferno - -from jinja2 import Template +from ipywidgets import interact, interactive, fixed, interact_manual, IntSlider +import ipywidgets as widgets +import matplotlib.pyplot as plt import numpy as np from . import config @@ -71,11 +68,11 @@ def resolve_translator(filename, translators): raise NameError("No translator found to open the given file %s", filename) -def plot_pulse_files(metafile, time=False): +def plot_pulse_files(metafile, time=False, backend='matplotlib'): ''' plot_pulse_files(metafile) - Helper function to plot a list of AWG files. A JS slider allows choice of sequence number. + Helper function to plot a list of AWG files. A jupyter slider widget allows choice of sequence number. ''' #If we only go one filename turn it into a list @@ -94,68 +91,25 @@ def plot_pulse_files(metafile, time=False): localname = os.path.split(fileNames[0])[1] sequencename = localname.split('-')[0] - data = {line_name: ColumnDataSource(data=dat) for line_name,dat in data_dicts.items()} - plot = Figure(title=sequencename, plot_width=1000) - if config.plotBackground: - plot.background_fill_color = config.plotBackground - if config.gridColor: - plot.xgrid.grid_line_color = config.gridColor - plot.ygrid.grid_line_color = config.gridColor - - if len(line_names) < 10: - # Colobrewer2 qualitative Set1 (http://colorbrewer2.org) - colors = brewer['Set1'][max(3, len(line_names))] - elif 9 < len(line_names) < 19: - # d3 Category20 for up to 18 channels - colors = d3['Category20'][len(line_names)] - else: - # 256 palette - colors = [Inferno[256][np.int(ct-1)] for ct in np.floor(np.linspace(0,256,len(line_names)))] - js_sources = data - for ct, k in enumerate(line_names): - k_ = k.replace("-", "_") - line = plot.line(data_dicts[k + "_1"]["x"], - data_dicts[k + "_1"]["y"], - color=colors[ct % len(colors)], - line_width=2, - legend=k.replace("_", "-")) - js_sources[k_] = line.data_source - - code_template = Template(""" - var seq_num = cb_obj.getv('value'); - // console.log(seq_num); - var all_data = { - {% for trace in trace_names %} - '{{trace}}': {{trace}}.getv('data'), - {% endfor %} - }; - {% for line in line_names %} - {{line}}['data'] = {'x': (all_data['{{line}}_'.concat(seq_num.toString())])['x'], - 'y': (all_data['{{line}}_'.concat(seq_num.toString())])['y']}; - {% endfor %} - """) - rendered = code_template.render( - line_names=line_names, - trace_names=list(data.keys())[:-len(line_names)] - ) - - callback = CustomJS( - args=dict(**js_sources), - code=rendered) - - if num_seqs > 1: - slider = Slider(start=1, - end=num_seqs, - value=1, - step=1, - title="Sequence", - callback=callback) - - layout = column(slider, plot) - show(layout) - else: - show(plot) - + if backend=='matplotlib': + fig = plt.figure() + ax = fig.add_subplot(1,1,1) + ax.set_title("Waveform Plotter") + lines = [] + for line_name in line_names: + dat = data_dicts[f"{line_name}_1"] + line, = ax.plot(dat['x'], dat['y'], label=line_name, linewidth=1.0) + lines.append(line) + plt.tight_layout() + plt.show() + + def update_plot(seq_ind): + for line, line_name in zip(lines, line_names): + dat = data_dicts[f"{line_name}_{seq_ind}"] + line.set_xdata(dat['x']) + line.set_ydata(dat['y']) + fig.canvas.draw() + interact(update_plot, seq_ind=IntSlider(min=1,max=num_seqs,step=1,value=1,description="Sequence Number")) def extract_waveforms(fileNames, nameDecorator='', time=False): line_names = [] diff --git a/QGL/__init__.py b/QGL/__init__.py index aa06418c..84728ba8 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -6,7 +6,7 @@ from .PulseSequencer import align from .ControlFlow import repeat, repeatall, qif, qwhile, qdowhile, qfunction, qwait, qsync, Barrier from .BasicSequences import * -from .Plotting import output_file, output_notebook, show, build_waveforms, plot_waveforms +from .Plotting import output_notebook #, show, build_waveforms, plot_waveforms from .PulseSequencePlotter import plot_pulse_files from .Tomography import state_tomo, process_tomo from .Scheduler import schedule diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 1d7bb765..c4c6ff95 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -178,7 +178,7 @@ def create_wf_vector(wfLib, seqs): idx = int(CACHE_LINE_LENGTH * ( (idx + CACHE_LINE_LENGTH) // CACHE_LINE_LENGTH)) wfVec = np.append(wfVec, - np.zeros(CACHE_LINE_LENGTH, + np.zeros(int(CACHE_LINE_LENGTH), dtype=np.int16)) offsets.append({}) @@ -1094,8 +1094,6 @@ def write_sequence_file(awgData, fileName): #Create the groups and datasets for chanct in range(2): - # chanStr = '/chan_{0}'.format(chanct + 1) - # chanGroup = FID.create_group(chanStr) #Write the waveformLib to file if wfInfo[chanct][0].size == 0: #If there are no waveforms, ensure that there is some element @@ -1131,10 +1129,10 @@ def start_new_seq(): file_version = struct.unpack('= 0.1 numpy >= 1.11.1 scipy >= 0.17.1 networkx >= 1.11 -bokeh >= 0.12.13 From a2715c66ed6d371799c0a3dcb8f5c3013835dcf1 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 11 Feb 2019 13:18:56 -0500 Subject: [PATCH 089/235] Replace bokeh with bqplot --- QGL/PulseSequencePlotter.py | 249 +++++++++++++++++++----------------- requirements.txt | 1 + setup.py | 3 +- 3 files changed, 136 insertions(+), 117 deletions(-) diff --git a/QGL/PulseSequencePlotter.py b/QGL/PulseSequencePlotter.py index b570dc09..5833a35b 100644 --- a/QGL/PulseSequencePlotter.py +++ b/QGL/PulseSequencePlotter.py @@ -25,9 +25,8 @@ import os.path import json from importlib import import_module -from ipywidgets import interact, interactive, fixed, interact_manual, IntSlider +from ipywidgets import interact, VBox, IntSlider import ipywidgets as widgets -import matplotlib.pyplot as plt import numpy as np from . import config @@ -68,7 +67,7 @@ def resolve_translator(filename, translators): raise NameError("No translator found to open the given file %s", filename) -def plot_pulse_files(metafile, time=False, backend='matplotlib'): +def plot_pulse_files(metafile, time=True, backend='bqplot'): ''' plot_pulse_files(metafile) @@ -92,24 +91,44 @@ def plot_pulse_files(metafile, time=False, backend='matplotlib'): sequencename = localname.split('-')[0] if backend=='matplotlib': - fig = plt.figure() - ax = fig.add_subplot(1,1,1) - ax.set_title("Waveform Plotter") + import matplotlib.pyplot as plt + def update_plot(seq_ind): + for line_name in line_names: + dat = data_dicts[f"{line_name}_{seq_ind}"] + plt.plot(dat['x'], dat['y'], label=line_name, linewidth=1.0) + interact(update_plot, seq_ind=IntSlider(min=1,max=num_seqs,step=1,value=1,description="Sequence Number")) + + elif backend=='bqplot': + from bqplot import DateScale, LinearScale, Axis, Lines, Figure, Tooltip + from bqplot.colorschemes import CATEGORY10, CATEGORY20 + from ipywidgets import interact, IntSlider + sx = LinearScale() + sy = LinearScale(min=-1.0, max=2*len(line_names)-1.0) + if time: + ax = Axis(label='Time (ns)', scale=sx) + else: + ax = Axis(label="Samples", scale=sx) + ay = Axis(label='Amplitude', scale=sy, orientation='vertical') + + colors = CATEGORY10 if len(line_names)<10 else CATEGORY20 lines = [] - for line_name in line_names: + tt = Tooltip(fields=['name'], labels=['Channel']) + x_mult = 1.0e9 if time else 1 + for i, line_name in enumerate(line_names): dat = data_dicts[f"{line_name}_1"] - line, = ax.plot(dat['x'], dat['y'], label=line_name, linewidth=1.0) - lines.append(line) - plt.tight_layout() - plt.show() + lines.append(Lines(labels=[line_name], x=x_mult*dat['x'], y=dat['y'], scales={'x': sx, 'y': sy}, + tooltip=tt, animate=False, colors=[colors[i]])) - def update_plot(seq_ind): + slider = IntSlider(min=1, max=num_seqs, step=1, description='Segment', value=1) + def segment_changed(change): for line, line_name in zip(lines, line_names): - dat = data_dicts[f"{line_name}_{seq_ind}"] - line.set_xdata(dat['x']) - line.set_ydata(dat['y']) - fig.canvas.draw() - interact(update_plot, seq_ind=IntSlider(min=1,max=num_seqs,step=1,value=1,description="Sequence Number")) + dat = data_dicts[f"{line_name}_{slider.value}"] + line.x = x_mult*dat['x'] + line.y = dat['y'] + slider.observe(segment_changed, 'value') + fig = Figure(marks=lines, axes=[ax, ay], title='Waveform Plotter',animation_duration=50) + return VBox([slider, fig]) + def extract_waveforms(fileNames, nameDecorator='', time=False): line_names = [] @@ -147,101 +166,101 @@ def extract_waveforms(fileNames, nameDecorator='', time=False): return line_names, num_seqs, data_dicts -def plot_pulse_files_compare(metafile1, metafile2, time=False): - ''' - plot_pulse_files_compare(fileNames1, fileNames2) - - Helper function to plot a list of AWG files. A JS slider allows choice of sequence number. - ''' - #If we only go one filename turn it into a list - fileNames1 = [] - fileNames2 = [] - - with open(metafile1, 'r') as FID: - meta_info1 = json.load(FID) - - for el in meta_info1["instruments"].values(): - # Accomodate seq_file per instrument and per channel - if isinstance(el, str): - fileNames1.append(el) - elif isinstance(el, dict): - for file in el.values(): - fileNames1.append(file) - - with open(metafile2, 'r') as FID: - meta_info2 = json.load(FID) - - for el in meta_info2["instruments"].values(): - # Accomodate seq_file per instrument and per channel - if isinstance(el, str): - fileNames2.append(el) - elif isinstance(el, dict): - for file in el.values(): - fileNames2.append(file) - - line_names1, num_seqs1, data_dicts1 = extract_waveforms(fileNames1, 'A', time=time) - line_names2, num_seqs2, data_dicts2 = extract_waveforms(fileNames2, 'B', time=time) - num_seqs = max(num_seqs1, num_seqs2) - data_dicts1.update(data_dicts2) - line_names = line_names1 + line_names2 - - localname = os.path.split(fileNames1[0])[1] - sequencename = localname.split('-')[0] - - data = {line_name: ColumnDataSource(data=dat) for line_name,dat in data_dicts1.items()} - plot = Figure(title=sequencename, plot_width=1000) - plot.background_fill_color = config.plotBackground - if config.gridColor: - plot.xgrid.grid_line_color = config.gridColor - plot.ygrid.grid_line_color = config.gridColor - - # Colobrewer2 qualitative Set1 (http://colorbrewer2.org) - colours = [ - "#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", - "#a65628", "#f781bf", "#999999" - ] - - js_sources = data - for ct, k in enumerate(line_names): - k_ = k.replace("-", "_") - line = plot.line(data_dicts1[k + "_1"]["x"], - data_dicts1[k + "_1"]["y"], - color=colours[ct % len(colours)], - line_width=2, - legend=k.replace("_", "-")) - js_sources[k_] = line.data_source - - code_template = Template(""" - var seq_num = cb_obj.getv('value'); - // console.log(seq_num); - var all_data = { - {% for trace in trace_names %} - '{{trace}}': {{trace}}.getv('data'), - {% endfor %} - }; - {% for line in line_names %} - {{line}}['data'] = {'x': (all_data['{{line}}_'.concat(seq_num.toString())])['x'], - 'y': (all_data['{{line}}_'.concat(seq_num.toString())])['y']}; - {% endfor %} - """) - rendered = code_template.render( - line_names=line_names, - trace_names=list(data.keys())[:-len(line_names)] - ) - - callback = CustomJS( - args=dict(**js_sources), - code=rendered) - - if num_seqs > 1: - slider = Slider(start=1, - end=num_seqs, - value=1, - step=1, - title="Sequence", - callback=callback) - - layout = column(slider, plot) - show(layout) - else: - show(plot) +# def plot_pulse_files_compare(metafile1, metafile2, time=False): +# ''' +# plot_pulse_files_compare(fileNames1, fileNames2) + +# Helper function to plot a list of AWG files. A JS slider allows choice of sequence number. +# ''' +# #If we only go one filename turn it into a list +# fileNames1 = [] +# fileNames2 = [] + +# with open(metafile1, 'r') as FID: +# meta_info1 = json.load(FID) + +# for el in meta_info1["instruments"].values(): +# # Accomodate seq_file per instrument and per channel +# if isinstance(el, str): +# fileNames1.append(el) +# elif isinstance(el, dict): +# for file in el.values(): +# fileNames1.append(file) + +# with open(metafile2, 'r') as FID: +# meta_info2 = json.load(FID) + +# for el in meta_info2["instruments"].values(): +# # Accomodate seq_file per instrument and per channel +# if isinstance(el, str): +# fileNames2.append(el) +# elif isinstance(el, dict): +# for file in el.values(): +# fileNames2.append(file) + +# line_names1, num_seqs1, data_dicts1 = extract_waveforms(fileNames1, 'A', time=time) +# line_names2, num_seqs2, data_dicts2 = extract_waveforms(fileNames2, 'B', time=time) +# num_seqs = max(num_seqs1, num_seqs2) +# data_dicts1.update(data_dicts2) +# line_names = line_names1 + line_names2 + +# localname = os.path.split(fileNames1[0])[1] +# sequencename = localname.split('-')[0] + +# data = {line_name: ColumnDataSource(data=dat) for line_name,dat in data_dicts1.items()} +# plot = Figure(title=sequencename, plot_width=1000) +# plot.background_fill_color = config.plotBackground +# if config.gridColor: +# plot.xgrid.grid_line_color = config.gridColor +# plot.ygrid.grid_line_color = config.gridColor + +# # Colobrewer2 qualitative Set1 (http://colorbrewer2.org) +# colours = [ +# "#e41a1c", "#377eb8", "#4daf4a", "#984ea3", "#ff7f00", "#ffff33", +# "#a65628", "#f781bf", "#999999" +# ] + +# js_sources = data +# for ct, k in enumerate(line_names): +# k_ = k.replace("-", "_") +# line = plot.line(data_dicts1[k + "_1"]["x"], +# data_dicts1[k + "_1"]["y"], +# color=colours[ct % len(colours)], +# line_width=2, +# legend=k.replace("_", "-")) +# js_sources[k_] = line.data_source + +# code_template = Template(""" +# var seq_num = cb_obj.getv('value'); +# // console.log(seq_num); +# var all_data = { +# {% for trace in trace_names %} +# '{{trace}}': {{trace}}.getv('data'), +# {% endfor %} +# }; +# {% for line in line_names %} +# {{line}}['data'] = {'x': (all_data['{{line}}_'.concat(seq_num.toString())])['x'], +# 'y': (all_data['{{line}}_'.concat(seq_num.toString())])['y']}; +# {% endfor %} +# """) +# rendered = code_template.render( +# line_names=line_names, +# trace_names=list(data.keys())[:-len(line_names)] +# ) + +# callback = CustomJS( +# args=dict(**js_sources), +# code=rendered) + +# if num_seqs > 1: +# slider = Slider(start=1, +# end=num_seqs, +# value=1, +# step=1, +# title="Sequence", +# callback=callback) + +# layout = column(slider, plot) +# show(layout) +# else: +# show(plot) diff --git a/requirements.txt b/requirements.txt index b50f7f6f..187883e3 100644 --- a/requirements.txt +++ b/requirements.txt @@ -2,3 +2,4 @@ bbndb >= 0.1 numpy >= 1.11.1 scipy >= 0.17.1 networkx >= 1.11 +bqplot >= 0.11.5 diff --git a/setup.py b/setup.py index a5ef0be3..6b6b9261 100644 --- a/setup.py +++ b/setup.py @@ -9,6 +9,5 @@ "numpy >= 1.11.1", "scipy >= 0.17.1", "networkx >= 1.11", - "bokeh >= 0.12.13", - # "pony >= 0.7.4", # This needs to be 0.7.4-dev + "bqplot >= 0.11.5", ]) From 12340d40b50271915cd8df665f8a87b47f6e478a Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 11 Feb 2019 15:56:19 -0500 Subject: [PATCH 090/235] Start tracking .aps2 files --- .gitattributes | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitattributes b/.gitattributes index 1bccc1fa..3477558f 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1 +1,2 @@ *.h5 filter=lfs diff=lfs merge=lfs -text +*.aps2 filter=lfs diff=lfs merge=lfs -text From c37060d1984c05bfd8cf2770cba653b9f70c6723 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 11 Feb 2019 15:59:07 -0500 Subject: [PATCH 091/235] Deleting APS2 h5 files --- tests/test_QGL.py | 2 +- tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/Echo/Echo-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/Echo/Echo-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 | 3 --- tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.h5 | 3 --- tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.h5 | 3 --- .../awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1_py27.h5 | 3 --- tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.h5 | 3 --- .../awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2_py27.h5 | 3 --- tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.h5 | 3 --- .../awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3_py27.h5 | 3 --- tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.h5 | 3 --- .../awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4_py27.h5 | 3 --- tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1_py27.h5 | 3 --- tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2_py27.h5 | 3 --- tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/Spec/Spec-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/Spec/Spec-APS2.h5 | 3 --- tests/test_data/awg/TestAPS2/T1/T1-APS1.h5 | 3 --- tests/test_data/awg/TestAPS2/T1/T1-APS2.h5 | 3 --- 69 files changed, 1 insertion(+), 205 deletions(-) delete mode 100644 tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/Echo/Echo-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/Echo/Echo-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 delete mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.h5 delete mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/Spec/Spec-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/Spec/Spec-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS2/T1/T1-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS2/T1/T1-APS2.h5 diff --git a/tests/test_QGL.py b/tests/test_QGL.py index b91156b0..b39fb5a1 100644 --- a/tests/test_QGL.py +++ b/tests/test_QGL.py @@ -55,7 +55,7 @@ def show(self): def build_filename(self, name): # utility for reading and writing - return self.testFileDirectory + self.fileHeader + '-' + name + '.h5' + return self.testFileDirectory + self.fileHeader + '-' + name + '.aps2' def write(self): # writes each sequence to a file in the test data directory a file header diff --git a/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.h5 b/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.h5 deleted file mode 100644 index b4146f64..00000000 --- a/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fbc0210f06102970cce4b843f2d11080ae9a47824cabc6bcc0c6cb7791675819 -size 14128 diff --git a/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.h5 b/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.h5 deleted file mode 100644 index 63de3f69..00000000 --- a/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b78617f09c3fc6e4e9ec43d35f512be6422750099e532c0a843e0f570b2ad7c1 -size 11472 diff --git a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.h5 b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.h5 deleted file mode 100644 index e8522425..00000000 --- a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f1aa4d2064d8c94bd5a2c861f5137c74b47bac9912de8e362650de79b9353632 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.h5 b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.h5 deleted file mode 100644 index 16aa0539..00000000 --- a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e656cf6d7f1ec5d289c44dfdf3395c21b5002cfa0d099106e28d1bb279ee998c -size 10664 diff --git a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.h5 b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.h5 deleted file mode 100644 index cf8c86e4..00000000 --- a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7248ddc03f87ba4062fb2cfdd5c8e9ff4c916a0bbff610de2ee4a13b3c9b09c9 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.h5 b/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.h5 deleted file mode 100644 index d5777ec0..00000000 --- a/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2e2e47343922a82e1258e6510001963cdd46acb7a7841565704c0175f255136a -size 10664 diff --git a/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.h5 b/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.h5 deleted file mode 100644 index 377ce4bc..00000000 --- a/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7d5be72267a5b599abcce2bcdf9968da5b9dfaab58cee5cf472c8fe8f3dce8cc -size 10664 diff --git a/tests/test_data/awg/TestAPS2/Echo/Echo-APS1.h5 b/tests/test_data/awg/TestAPS2/Echo/Echo-APS1.h5 deleted file mode 100644 index 176b812c..00000000 --- a/tests/test_data/awg/TestAPS2/Echo/Echo-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4321e2efd619bb40368ad6238687cd97f465c94909f3969a4d80a5a09de36efc -size 11464 diff --git a/tests/test_data/awg/TestAPS2/Echo/Echo-APS2.h5 b/tests/test_data/awg/TestAPS2/Echo/Echo-APS2.h5 deleted file mode 100644 index a62f3b9d..00000000 --- a/tests/test_data/awg/TestAPS2/Echo/Echo-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c2354707a7b3471650d08145218677beb569d9ea2d87283b56ad84473dfed599 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 deleted file mode 100644 index ab15aa85..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:074d02801fc6b663db6f9250ceb639ca7b2e755ddb04ae70f3b9dd87e50c586c -size 12792 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 deleted file mode 100644 index 9d8b12af..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61084d6ac1164b7568c446759cb0baca6d850926162b7adc3073d4caa2aed0c7 -size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 deleted file mode 100644 index 0ab91628..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6749480ab0b75672d39dd6005238719b1e76e3b3f9e6814894220076158ee1aa -size 10800 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 deleted file mode 100644 index 9d8b12af..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:61084d6ac1164b7568c446759cb0baca6d850926162b7adc3073d4caa2aed0c7 -size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 deleted file mode 100644 index fae1d541..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e7da7ea9ae19ea57b6e1cc5783c4ebb53ad2522ef51979d1fb656738ecc0aaf6 -size 14192 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 deleted file mode 100644 index 8696d76b..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aace28a1695a9111804ab0bec5d783d482b835646ef86ca46e8e72b95092c8eb -size 12792 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 deleted file mode 100644 index 0779fc89..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:31849839f1cef008563c60475daae27687e16d87fd14f793eabdf80b4140cdc6 -size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 deleted file mode 100644 index 3944eb32..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f4e95177c552f582aa32d9fac036db4cc9acd19214eabb4d74bec2a31fbb885f -size 11296 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 deleted file mode 100644 index 9220854d..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f2000631a2233d3c5b19d2fa7e53d5c6b65f3a09433bd0b8aedc85906d9bccc3 -size 11344 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 deleted file mode 100644 index 043f2ada..00000000 --- a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4f8df32e2cf8c12ad3b48430f82500f8aa5fcdbf20d321825ae1f487d6f091db -size 22240 diff --git a/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.h5 b/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.h5 deleted file mode 100644 index a02c1072..00000000 --- a/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:83900cc7545b582b4094661ffe46cdf13ae1b0c5fe0f26b81889c85e948482a6 -size 36144 diff --git a/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.h5 b/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.h5 deleted file mode 100644 index 35bb1250..00000000 --- a/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:cc3a30d96dafb22cd52d508ed0770c129fdf99338b33471480580fdf959eeed6 -size 23776 diff --git a/tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.h5 b/tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.h5 deleted file mode 100644 index b0a3a6a7..00000000 --- a/tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:86e48fbb5d9ee98c3c04a98c7a473d5ff61d41be88fbb73c7ad9cf4e5a005ce4 -size 12144 diff --git a/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.h5 b/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.h5 deleted file mode 100644 index 03a088e7..00000000 --- a/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9adb85c5ce6cb9816addc5e8604b27a8111e6b28dde262f63dfc5057f9d7a06d -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.h5 b/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.h5 deleted file mode 100644 index c2c2df99..00000000 --- a/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:915e457884f2c97a47c28d3d8c27390c9c8058262ce6394ccdf0b1c2d744abbf -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.h5 b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.h5 deleted file mode 100644 index c51d3682..00000000 --- a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:416c37f8cdd7f8ce8bf1abe4b72a4173e3a661148e0db3aefe4d5665b1f0a147 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.h5 b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.h5 deleted file mode 100644 index 16aa0539..00000000 --- a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e656cf6d7f1ec5d289c44dfdf3395c21b5002cfa0d099106e28d1bb279ee998c -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.h5 b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.h5 deleted file mode 100644 index cee1a3cd..00000000 --- a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:effecd5635e103d6cfd24c33e8a2433c044c9ea8738f726d20db2d3a8688033b -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.h5 b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.h5 deleted file mode 100644 index 5c356fdd..00000000 --- a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8ff47cb0de965b5eeee007073e18cf1595db07704bea14860301b4ebc7269558 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.h5 b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.h5 deleted file mode 100644 index b43355ab..00000000 --- a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ad29c5d9ad6bdcf2d71e26167aced2f450382754cd23d764ad21165f2b268f15 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.h5 b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.h5 deleted file mode 100644 index 2276be6d..00000000 --- a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:b101668cd5d7ea4ced46a8d357ce9145984d66bf18610f222df20666846fcdaf -size 10664 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.h5 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.h5 deleted file mode 100644 index fa599be4..00000000 --- a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:7c311beb3a70d927d1939705577029ae1939822593558d2cf1ba50cb3351cda7 -size 11744 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.h5 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.h5 deleted file mode 100644 index b9f33eb5..00000000 --- a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81a88bcc43f757356ad54647b515bb508c7fd4767da4f806c48f20fc082af080 -size 10824 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.h5 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.h5 deleted file mode 100644 index 69a02bfb..00000000 --- a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8960fdf72531618dbacf7c8b1c10334184a4a7ef6ded1c0fdadce1c002cfae77 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.h5 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.h5 deleted file mode 100644 index b9f33eb5..00000000 --- a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:81a88bcc43f757356ad54647b515bb508c7fd4767da4f806c48f20fc082af080 -size 10824 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.h5 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.h5 deleted file mode 100644 index 099c3395..00000000 --- a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:5d863d35d6ecf34db2e931f291c41f1d5e89a76c6dc56218f206397daf2eb411 -size 11192 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.h5 deleted file mode 100644 index 3c3c7192..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d054d3bf07b82bcd8fceb169441aa52653742fe6be83b2eb799fd339675ee120 -size 11168 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.h5 b/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.h5 deleted file mode 100644 index 8ad9d24c..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14db8318fc956cc9428038237eb83de6ef9d7dd8e343a627f5af6d8be8f2808b -size 10664 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.h5 deleted file mode 100644 index 3c3c7192..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d054d3bf07b82bcd8fceb169441aa52653742fe6be83b2eb799fd339675ee120 -size 11168 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.h5 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.h5 deleted file mode 100644 index 8ad9d24c..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14db8318fc956cc9428038237eb83de6ef9d7dd8e343a627f5af6d8be8f2808b -size 10664 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.h5 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.h5 deleted file mode 100644 index 4eac161e..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:969244eeb80756e810991823154a170fd7c781eeb6e6458a5c29e72851547e93 -size 10728 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.h5 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.h5 deleted file mode 100644 index 8ad9d24c..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:14db8318fc956cc9428038237eb83de6ef9d7dd8e343a627f5af6d8be8f2808b -size 10664 diff --git a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.h5 deleted file mode 100644 index 15c902a5..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:d0775ac3780f687769e78cad08d9b7d92bf95e3e3c2f3050ce58a974f5fda4c7 -size 11296 diff --git a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.h5 b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.h5 deleted file mode 100644 index 4c178a11..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3c7dc446b904881261db1c6633dd34ebae89e9c2ffc6889a54581bfdf7d25697 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.h5 b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.h5 deleted file mode 100644 index 33dea5d2..00000000 --- a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:bdc238f311f839735033b037991ba9f78bc867ba8b1c6c8a15f235eaa66a0837 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.h5 deleted file mode 100644 index 5259eba8..00000000 --- a/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a339b76ebcc9d086cd3803a89b1c5e6c90b3fd06e103e450abe6d74648827995 -size 141600 diff --git a/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.h5 b/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.h5 deleted file mode 100644 index bd8942d8..00000000 --- a/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ec5f1a8ba8396b3ba1319ce825fc8e4ec0d9fc6415768905f758a15a54193f4b -size 10664 diff --git a/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.h5 b/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.h5 deleted file mode 100644 index 40d604b3..00000000 --- a/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ef6338bddfdcab213e12581b6a97b1316884838167748b0c5e98cd4330b64b22 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.h5 b/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.h5 deleted file mode 100644 index ac0be242..00000000 --- a/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c3cba432c0f7d9b7207a9ce43f8301c743c630271106d2a80712c89e75c46ef5 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.h5 b/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.h5 deleted file mode 100644 index 5fd4a6cb..00000000 --- a/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43b14db680f80a369c2540c565cefac2a2870aea59c7bad31c05d2d74956c270 -size 49464 diff --git a/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.h5 b/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.h5 deleted file mode 100644 index 6583bfa6..00000000 --- a/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:54c4f9ef853fedffb8fa8715d725328e4af6d73e6027e8a38916ab914d76e729 -size 31696 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.h5 deleted file mode 100644 index d2e4d179..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dd966f8d48d3cfa94237cc2c112b70157d35230ca2dcd4664bf4c4c731d3a061 -size 109792 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1_py27.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1_py27.h5 deleted file mode 100644 index dfca1f76..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:afa8cc3c99fea314c9e01d7159c8222932760cf47bc93ff6780452ebd4e7e14f -size 109792 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.h5 deleted file mode 100644 index 13bd0b3a..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:222857cb4e7df9c271e1583a94d1e6696dd2c29104e7ee60dcfeace5b8af541a -size 48752 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2_py27.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2_py27.h5 deleted file mode 100644 index 2b9cb45f..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09bf8d013355b15c3f97a641bb2d70eed3e28f4e0d678c718048634d447e60c3 -size 48752 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.h5 deleted file mode 100644 index bb804166..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:2af3590d082ccb86ee8593c3913f8b505dba80f7fae8a21fa7d763b5e753bd36 -size 102752 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3_py27.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3_py27.h5 deleted file mode 100644 index bc8caa68..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e42789e76e1d2e544847ddb2f4131198188a8aa5971097a537dd354fd8149bb1 -size 102752 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.h5 deleted file mode 100644 index 13bd0b3a..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:222857cb4e7df9c271e1583a94d1e6696dd2c29104e7ee60dcfeace5b8af541a -size 48752 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4_py27.h5 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4_py27.h5 deleted file mode 100644 index b3de0972..00000000 --- a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:51493c82b6a380853c1644469efff8e2234c0233e48b5508759a066e1f16fc1b -size 48752 diff --git a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.h5 b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.h5 deleted file mode 100644 index 062da429..00000000 --- a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd2e0d3b890dc1010ad4b55381c11b6f7744a8c58106c950d7a445620fdf8c44 -size 122376 diff --git a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1_py27.h5 b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1_py27.h5 deleted file mode 100644 index d86f0f00..00000000 --- a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:43e610294922c1e73d59fcdb0c6473d9f9c2338c3d8351c2dd6a2eda075fa55d -size 122376 diff --git a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.h5 b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.h5 deleted file mode 100644 index dacb3ba2..00000000 --- a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9e805e568677dda8a06e7070204af604ed0b84926b4cf1fc0dbe617d8a13b5a9 -size 77248 diff --git a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2_py27.h5 b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2_py27.h5 deleted file mode 100644 index 23bf2dfa..00000000 --- a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8792254cb7b487fdc6cde009a8950654b674d4a31903f875f056399131db928c -size 77248 diff --git a/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.h5 b/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.h5 deleted file mode 100644 index 6d9c8d34..00000000 --- a/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fecac08fc9b57eb04f0d4aaf88ac64479e74860b93a5576452ef3cb4cb68ffef -size 10664 diff --git a/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.h5 b/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.h5 deleted file mode 100644 index 5d400603..00000000 --- a/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e4125f49979cef1af9af1aa16d7795f50be4cb39ae02f60aca1a79a31eab3a93 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/Spec/Spec-APS1.h5 b/tests/test_data/awg/TestAPS2/Spec/Spec-APS1.h5 deleted file mode 100644 index 0f6b1e0d..00000000 --- a/tests/test_data/awg/TestAPS2/Spec/Spec-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:9d52de2c3596cc0f61bf8dee2c224d26b5b6bdc378332001d0eea00a15dfe328 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/Spec/Spec-APS2.h5 b/tests/test_data/awg/TestAPS2/Spec/Spec-APS2.h5 deleted file mode 100644 index e89591a9..00000000 --- a/tests/test_data/awg/TestAPS2/Spec/Spec-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:4fed627361f76485e09a21c1e72186e64b710ca9a6bf1e71aff1b5cbefebf108 -size 10664 diff --git a/tests/test_data/awg/TestAPS2/T1/T1-APS1.h5 b/tests/test_data/awg/TestAPS2/T1/T1-APS1.h5 deleted file mode 100644 index 5c23cfc1..00000000 --- a/tests/test_data/awg/TestAPS2/T1/T1-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1ae2feab747be8b5ef6804c3cae154b6099dc30d22c0dd4f62b9bdc121a7beee -size 10664 diff --git a/tests/test_data/awg/TestAPS2/T1/T1-APS2.h5 b/tests/test_data/awg/TestAPS2/T1/T1-APS2.h5 deleted file mode 100644 index 126007b4..00000000 --- a/tests/test_data/awg/TestAPS2/T1/T1-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:09d114f4c75921bf7aea9db671bbf3618046c1965a4229e405ad0c8d2f7e7805 -size 10664 From 16dd0c1357495ba39eac89f54c18731b67c92e5b Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 13:57:22 -0500 Subject: [PATCH 092/235] Update test_QGL --- QGL/Plotting.py | 4 --- QGL/__init__.py | 2 +- tests/test_QGL.py | 82 +++++++++++++++++++++++++++++------------------ 3 files changed, 51 insertions(+), 37 deletions(-) diff --git a/QGL/Plotting.py b/QGL/Plotting.py index 254c1568..8051cb7d 100644 --- a/QGL/Plotting.py +++ b/QGL/Plotting.py @@ -26,10 +26,6 @@ import warnings from . import config -def output_notebook(): - import matplotlib - matplotlib.use("nbagg") - def build_waveforms(seq): # import here to avoid circular imports from . import Compiler, PulseSequencer, BlockLabel, ControlFlow diff --git a/QGL/__init__.py b/QGL/__init__.py index 84728ba8..82defa84 100644 --- a/QGL/__init__.py +++ b/QGL/__init__.py @@ -6,7 +6,7 @@ from .PulseSequencer import align from .ControlFlow import repeat, repeatall, qif, qwhile, qdowhile, qfunction, qwait, qsync, Barrier from .BasicSequences import * -from .Plotting import output_notebook #, show, build_waveforms, plot_waveforms +from .Plotting import build_waveforms #, show, , plot_waveforms from .PulseSequencePlotter import plot_pulse_files from .Tomography import state_tomo, process_tomo from .Scheduler import schedule diff --git a/tests/test_QGL.py b/tests/test_QGL.py index b39fb5a1..c5db5b52 100644 --- a/tests/test_QGL.py +++ b/tests/test_QGL.py @@ -3,8 +3,10 @@ import numpy as np import time import os.path - +import struct +from bbndb.qgl import PhysicalChannel, LogicalChannel, Measurement from QGL import * +import h5py # Waveform numpy assert_allclose Test for QGL # @@ -33,7 +35,7 @@ def __init__(self): # all available sequences (which are defined in subclasses) self.generate() self.compile() - self.load() + self.load() def generate(self): # this function should be overridden in a subclass to define @@ -61,22 +63,23 @@ def write(self): # writes each sequence to a file in the test data directory a file header # which may be overridden in the subclasses for name, waveform in self.waveforms.items(): + fileName = self.build_filename(name) - #Open the HDF5 file if os.path.isfile(fileName): os.remove(fileName) - with h5py.File(fileName, 'w') as FID: - FID['/'].attrs['Type'] = 'test_case' - FID['/'].attrs['Version'] = 1.0 + with open(fileName, 'wb') as FID: + FID.write(b'TEST') # target + FID.write(np.float32(1.0).tobytes()) # Version + FID.write(np.float32(1.0).tobytes()) # minimum firmware version + FID.write(np.uint16(len(waveform)).tobytes()) # number of channels - #Create the groups and datasets channels = waveform.keys() - chanStr = '/channels' - chanGroup = FID.create_group(chanStr) for channel in channels: - channelStr = channel.label - FID.create_dataset('/' + chanStr + '/' + channelStr, - data=waveform[channel]) + channel.label.ljust(32,"#").encode("utf-8") + FID.write(channel.label.ljust(32,"#").encode("utf-8")) + FID.write(np.uint64(waveform[channel].size).tobytes()) + FID.write(np.complex128(waveform[channel]).tobytes()) # waveform data length for channel + def load(self): # attempts to load valid waveforms for each of the sequences test cases @@ -89,22 +92,30 @@ def load(self): caseName, fileName)) continue # ----- - # print( "\n\rDBG::Calling \"with h5py.File( {0}, 'r')\"...".format( fileName) ) + # print( "\n\rDBG::Calling \"with open({0}, 'wb')\"...".format( fileName) ) # ----- # The following pulls in Git Large File Storage (LFS) data files - # from cached signature references; if the h5py.File call fails + # from cached signature references; if the open call fails # with an OSError, double-check git-lfs library installation (in # addition to git) -- # - # Where git-lfs was NOT installed the h5py.File() call was observed + # Where git-lfs was NOT installed the open call was observed # returning: # # OSError: Unable to open file (file signature not found)) # - with h5py.File(fileName, 'r') as FID: - # print( "DBG::FID: {0}".format( FID)) - for name, waveform in FID['/channels'].items(): - validWaveform[name] = waveform[:] + with open(fileName, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + file_version = struct.unpack(' Date: Tue, 12 Feb 2019 21:15:54 -0500 Subject: [PATCH 093/235] Now passing test_sequence for APS2, excepting GST tests --- tests/test_Sequences.py | 470 ++++++++++++---------------------------- 1 file changed, 136 insertions(+), 334 deletions(-) diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index 1e145046..2c0cc836 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -1,4 +1,3 @@ -import h5py import numpy as np import unittest, time, os, random, sys @@ -37,10 +36,12 @@ def finalize_map(self, mapping): for name, value in mapping.items(): self.channels[name].phys_chan = self.channels[value] - ChannelLibraries.channelLib = ChannelLibraries.ChannelLibrary(blank=True) - ChannelLibraries.channelLib.channelDict = self.channels - ChannelLibraries.channelLib.build_connectivity_graph() - + self.cl = ChannelLibrary(db_resource_name=":memory:") + self.cl.clear() + self.cl.session.add_all(self.channels.values()) + for chan in self.channels.values(): + chan.channel_db = self.cl.channelDatabase + self.cl.update_channelDict() (self.q1, self.q2) = self.get_qubits() def assign_channels(self): @@ -133,21 +134,9 @@ def compare_sequences(self, seqDir): filenames = os.listdir(truthDirectory) - #look for py27 versions - py27_files = [f for f in filenames if f[-8:] == "_py27.h5"] - if py27_files: - if sys.version_info[0] > 2: - #strip them out for python3 (and above?) - filenames = [f for f in filenames if f[-8:] != "_py27.h5"] - else: - #otherwise strip the non "_py27" version - filenames = [f for f in filenames - if f.replace(".h5", "_py27.h5") not in py27_files] - for filename in filenames: truthFile = os.path.join(truthDirectory, filename) - testFile = os.path.join(searchDirectory, filename.replace("_py27", - "")) + testFile = os.path.join(searchDirectory, filename) self.assertTrue( os.path.isfile(truthFile), @@ -185,7 +174,6 @@ def compare_sequence(self, seqA, seqB, errorHeader): for ta in seqA]) if len(seqA) else np.empty(0) wfB = np.concatenate([ta[1] * np.ones(int(ta[0])) for ta in seqB]) if len(seqB) else np.empty(0) - self.assertTrue( len(wfA) == len(wfB), "{} size {} != size {}".format( errorHeader, len(wfA), len(wfB))) @@ -390,6 +378,7 @@ def test_RB_SimultaneousRB_AC(self): SimultaneousRB_AC((self.q1, self.q2), (seqs1, seqs2)) self.compare_sequences('RB') + @unittest.skip("Need to update to new pygsti API") def test_1Q_GST(self): if istravis: @@ -416,6 +405,7 @@ def test_1Q_GST(self): filenames = compile_to_hardware(seqs, 'GST/GST') self.compare_sequences('GST') + @unittest.skip("Need to update to new pygsti API") def test_2Q_GST(self): if istravis: @@ -461,7 +451,7 @@ class APS2Helper(AWGTestHelper): def setUp(self): AWGTestHelper.__init__(self, APS2Pattern) for name in ['APS1', 'APS2', 'APS3', 'APS4', 'APS5', 'APS6']: - channelName = name + '-12' + channelName = name + '-1' channel = PhysicalQuadratureChannel(label=channelName) channel.sampling_rate = 1.2e9 channel.instrument = name @@ -469,27 +459,27 @@ def setUp(self): self.channels[channelName] = channel for m in range(1, 5): - channelName = "{0}-12m{1}".format(name, m) + channelName = "{0}-m{1}".format(name, m) channel = PhysicalMarkerChannel(label=channelName) channel.sampling_rate = 1.2e9 channel.instrument = name channel.translator = 'APS2Pattern' self.channels[channelName] = channel - mapping = {'digitizerTrig': 'APS1-12m1', - 'slave_trig': 'APS1-12m2', - 'q1': 'APS1-12', - 'q1-gate': 'APS1-12m3', - 'M-q1': 'APS2-12', - 'M-q1-gate': 'APS2-12m1', - 'q2': 'APS3-12', - 'q2-gate': 'APS3-12m1', - 'M-q2': 'APS4-12', - 'M-q2-gate': 'APS4-12m1', - 'cr': 'APS5-12', - 'cr-gate': 'APS5-12m1', - 'M-q1q2': 'APS6-12', - 'M-q1q2-gate': 'APS6-12m1'} + mapping = {'digitizerTrig': 'APS1-m1', + 'slave_trig': 'APS1-m2', + 'q1': 'APS1-1', + 'q1-gate': 'APS1-m3', + 'M-q1': 'APS2-1', + 'M-q1-gate': 'APS2-m1', + 'q2': 'APS3-1', + 'q2-gate': 'APS3-m1', + 'M-q2': 'APS4-1', + 'M-q2-gate': 'APS4-m1', + 'cr': 'APS5-1', + 'cr-gate': 'APS5-m1', + 'M-q1q2': 'APS6-1', + 'M-q1q2-gate': 'APS6-m1'} self.finalize_map(mapping) @@ -506,311 +496,123 @@ def test_mux_CR(self): self.channels['cr'].phys_chan = self.channels['q1'].phys_chan self.channels['q1'].frequency = 100e6 self.channels['cr'].frequency = 200e6 - ChannelLibraries.channelLib.build_connectivity_graph() + self.cl.update_channelDict() seqs = [[CNOT_CR(self.q1, self.q2)]] filenames = compile_to_hardware(seqs, 'CNOT_CR_mux/CNOT_CR_mux') self.compare_sequences('CNOT_CR_mux') -class TestAPS1(unittest.TestCase, AWGTestHelper, TestSequences): - def setUp(self): - AWGTestHelper.__init__(self, APSPattern) - for name in ['APS1', 'APS2', 'APS3']: - for ch in ['12', '34']: - channelName = name + '-' + ch - channel = PhysicalQuadratureChannel(label=channelName) - channel.sampling_rate = 1.2e9 - channel.instrument = name - channel.translator = 'APSPattern' - self.channels[channelName] = channel - - for m in range(1, 5): - channelName = "{0}-{1}m1".format(name, m) - channel = PhysicalMarkerChannel(label=channelName) - channel.sampling_rate = 1.2e9 - channel.instrument = name - channel.translator = 'APSPattern' - self.channels[channelName] = channel - - mapping = {'digitizerTrig': 'APS1-1m1', - 'slave_trig': 'APS1-2m1', - 'q1': 'APS1-12', - 'M-q1': 'APS1-34', - 'M-q1-gate': 'APS1-3m1', - 'q1-gate': 'APS1-4m1', - 'q2': 'APS2-12', - 'M-q2': 'APS2-34', - 'M-q2-gate': 'APS2-1m1', - 'q2-gate': 'APS2-2m1', - 'cr': 'APS3-12', - 'cr-gate': 'APS3-1m1', - 'M-q1q2': 'APS3-34', - 'M-q1q2-gate': 'APS3-2m1'} - - # override trigger lengths on APS1 to get single blips - self.channels['slave_trig'].pulse_params['length'] = 0.833e-9 - self.channels['digitizerTrig'].pulse_params['length'] = 0.833e-9 - self.finalize_map(mapping) - - def compare_file_data(self, testFile, truthFile): - ''' - Override the method in AWGTestHelper so that we can special-case marker comparison - ''' - awgData = self.read_function(testFile) - truthData = self.read_function(truthFile) - - awgDataLen = len(awgData) - truthDataLen = len(truthData) - - self.assertTrue(awgDataLen == truthDataLen, - "Expected {0} sequences in file. Found {1}.".format( - truthDataLen, awgDataLen)) - - for name in truthData: - self.assertTrue( - name in awgData, - "Expected channel {0} not found in file {1}".format(name, - testFile)) - isMarker = ('m' == name[-2]) - - for x in range(len(truthData[name])): - seqA = np.array(truthData[name][x]) - seqB = np.array(awgData[name][x]) - if isMarker: - self.compare_marker_sequence( - seqA, seqB, - "\nFile {0} =>\nChannel {1} Sequence {2}".format( - testFile, name, x)) - else: - self.compare_sequence( - seqA, seqB, - "\nFile {0} =>\nChannel {1} Sequence {2}".format( - testFile, name, x)) - - def compare_marker_sequence(self, seqA, seqB, errorHeader): - markerDistanceTolerance = 4 - self.assertTrue(seqA.size == seqB.size, - "{0} size {1} != size {2}".format( - errorHeader, str(seqA.size), str(seqB.size))) - - # convert sequences to locations of blips - idxA = np.where(seqA)[0] - idxB = np.where(seqB)[0] - self.assertTrue( - len(idxA) == len(idxB), - "{0}.\nNumber of blips did not match: {1} != {2}".format( - errorHeader, len(idxA), len(idxB))) - # compare the blip locations element-wise - if len(idxA) > 0: - diff = np.abs(idxA - idxB) - else: - diff = np.array([0]) - self.assertTrue( - max(diff) <= markerDistanceTolerance, - "{0}\nMismatches: {1}".format(errorHeader, diff.nonzero()[0])) - - @unittest.expectedFailure - def test_Rabi_RabiWidth(self): - """ test_Rabi_RabiWidth is expected to fail on APS1 with the following error: - AssertionError: Oops! You have exceeded the waveform memory of the APS - """ - TestSequences.test_Rabi_RabiWidth(self) - - @unittest.expectedFailure - def test_RB_SimultaneousRB_AC(self): - """ test_RB_SimultaneousRB_AC is expected to fail on APS1 with the following error: - AssertionError: Oops! You have exceeded the waveform memory of the APS - """ - TestSequences.test_RB_SimultaneousRB_AC(self) - - -class TestTek5014(unittest.TestCase, AWGTestHelper, TestSequences): - def setUp(self): - AWGTestHelper.__init__(self, TekPattern) - for name in ['TEK1', 'TEK2']: - for ch in ['12', '34']: - channelName = name + '-' + ch - channel = PhysicalQuadratureChannel(label=channelName) - channel.sampling_rate = 1.2e9 - channel.instrument = name - channel.translator = 'TekPattern' - self.channels[channelName] = channel - - for m in ['1m1', '1m2', '2m1', '2m2', '3m1', '3m2', '4m1', '4m2']: - channelName = "{0}-{1}".format(name, m) - channel = PhysicalMarkerChannel(label=channelName) - channel.sampling_rate = 1.2e9 - channel.instrument = name - channel.translator = 'TekPattern' - self.channels[channelName] = channel - - mapping = {'digitizerTrig': 'TEK1-1m2', - 'slave_trig': 'TEK1-2m2', - 'q1': 'TEK1-12', - 'M-q1': 'TEK1-12', - 'M-q1-gate': 'TEK1-1m1', - 'q1-gate': 'TEK1-2m1', - 'q2': 'TEK1-34', - 'M-q2': 'TEK1-34', - 'M-q2-gate': 'TEK1-3m1', - 'q2-gate': 'TEK1-4m1', - 'cr': 'TEK2-12', - 'cr-gate': 'TEK2-1m1', - 'M-q1q2': 'TEK2-34', - 'M-q1q2-gate': 'TEK2-2m1'} - - self.finalize_map(mapping) - - @unittest.skip("Tek5014 unused in years") - def test_misc_seqs2(self): - """ Fails due to a divide by zero - File "C:\Projects\Q\lib\PyQLab\QGL\TekPattern.py", line 77, in merge_waveform - for entry in chAB['linkList'][n % len(chAB['linkList'])]: - ZeroDivisionError: integer division or modulo by zero - """ - TestSequences.test_misc_seqs2(self) - - @unittest.skip("Tek5014 unused in years") - def test_misc_seqs5(self): - """ Fails due to a divide by zero - File "C:\Projects\Q\lib\PyQLab\QGL\TekPattern.py", line 77, in merge_waveform - for entry in chAB['linkList'][n % len(chAB['linkList'])]: - ZeroDivisionError: integer division or modulo by zero - """ - TestSequences.test_misc_seqs5(self) - - # multiple tests will fail with an attribute error: - # AttributeError: 'Wait' object has no attribute 'isTimeAmp' at line 78 - # in TekPattern.py in merge_waveform - - @unittest.skip("Tek5014 unused in years") - def test_misc_seqs1(self): - TestSequences.test_misc_seqs1(self) - - @unittest.skip("Tek5014 unused in years") - def test_misc_seqs3(self): - TestSequences.test_misc_seqs3(self) - - @unittest.skip("Tek5014 unused in years") - def test_misc_seqs4(self): - TestSequences.test_misc_seqs4(self) - - @unittest.skip("Tek5014 unused in years") - def test_mux_CR(self): - TestSequences.test_mux_CR(self) - - @unittest.skip("Tek5014 unused in years") - def test_AllXY(self): - TestSequences.test_AllXY(self) - - @unittest.skip("Tek5014 unused in years") - def test_CR_PiRabi(self): - TestSequences.test_CR_PiRabi(self) - - @unittest.skip("Tek5014 unused in years") - def test_CR_EchoCRLen(self): - TestSequences.test_CR_EchoCRLen(self) - - @unittest.skip("Tek5014 unused in years") - def test_CR_EchoCRPhase(self): - TestSequences.test_CR_EchoCRPhase(self) - - @unittest.skip("Tek5014 unused in years") - def test_Decoupling_HannEcho(self): - TestSequences.test_Decoupling_HannEcho(self) - - @unittest.skip("Tek5014 unused in years") - def test_Decoupling_CPMG(self): - TestSequences.test_Decoupling_CPMG(self) - - @unittest.skip("Tek5014 unused in years") - def test_FlipFlop(self): - TestSequences.test_FlipFlop(self) - - @unittest.skip("Tek5014 unused in years") - def test_T1T2_InversionRecovery(self): - TestSequences.test_T1T2_InversionRecovery(self) - - @unittest.skip("Tek5014 unused in years") - def test_T1T2_Ramsey(self): - TestSequences.test_T1T2_Ramsey(self) - - @unittest.skip("Tek5014 unused in years") - def test_SPAM(self): - TestSequences.test_SPAM(self) - - @unittest.skip("Tek5014 unused in years") - def test_Rabi_RabiAmp(self): - TestSequences.test_Rabi_RabiAmp(self) - - @unittest.skip("Tek5014 unused in years") - def test_Rabi_RabiWidth(self): - TestSequences.test_Rabi_RabiWidth(self) - - @unittest.skip("Tek5014 unused in years") - def test_Rabi_RabiAmp_NQubits(self): - TestSequences.test_Rabi_RabiAmp_NQubits(self) - - @unittest.skip("Tek5014 unused in years") - def test_Rabi_RabiAmpPi(self): - TestSequences.test_Rabi_RabiAmpPi(self) - - @unittest.skip("Tek5014 unused in years") - def test_Rabi_SingleShot(self): - TestSequences.test_Rabi_SingleShot(self) - - @unittest.skip("Tek5014 unused in years") - def test_Rabi_PulsedSpec(self): - TestSequences.test_Rabi_PulsedSpec(self) - - @unittest.skip("Tek5014 unused in years") - def test_RB_SingleQubitRB(self): - TestSequences.test_RB_SingleQubitRB(self) - - @unittest.skip("Tek5014 unused in years") - def test_RB_SimultaneousRB_AC(self): - TestSequences.test_RB_SimultaneousRB_AC(self) - - @unittest.skip("Tek5014 unused in years") - def test_1Q_GST(self): - TestSequences.test_1Q_GST(self) - - @unittest.skip("Tek5014 unused in years") - def test_2Q_GST(self): - TestSequences.test_2Q_GST(self) -# class TestTek7000(unittest.TestCase, AWGTestHelper, TestSequences): - -# def setUp(self): -# AWGTestHelper.__init__(self, TekPattern.read_Tek_file) -# for name in ['TEK1', 'TEK2', 'TEK3', 'TEK4', 'TEK5']: -# self.instruments[name] = Tek7000(label=name) - -# for ch in ['12']: -# channelName = name + '-' + ch -# channel = PhysicalQuadratureChannel(label=channelName) -# channel.instrument = self.instruments[name] -# self.channels[channelName] = channel - -# for m in ['1m1', '1m2', '2m1', '2m2']: -# channelName = "{0}-{1}".format(name,m) -# channel = PhysicalMarkerChannel(label=channelName) -# channel.instrument = self.instruments[name] -# self.channels[channelName] = channel - -# mapping = { 'digitizerTrig' :'TEK1-1m2', -# 'slave_trig' :'TEK1-2m2', -# 'q1' :'TEK1-12', -# 'M-q1' :'TEK2-12', -# 'M-q1-gate' :'TEK1-1m1', -# 'q1-gate' :'TEK1-2m1', -# 'q2' :'TEK3-12', -# 'M-q2' :'TEK4-12', -# 'M-q2-gate' :'TEK2-1m1', -# 'q2-gate' :'TEK2-2m1', -# 'cr' :'TEK5-12', -# 'cr-gate' :'TEK5-1m1'} -# self.finalize_map(mapping) +# class TestAPS1(unittest.TestCase, AWGTestHelper, TestSequences): +# def setUp(self): +# AWGTestHelper.__init__(self, APSPattern) +# for name in ['APS1', 'APS2', 'APS3']: +# for ch in ['12', '34']: +# channelName = name + '-' + ch +# channel = PhysicalQuadratureChannel(label=channelName) +# channel.sampling_rate = 1.2e9 +# channel.instrument = name +# channel.translator = 'APSPattern' +# self.channels[channelName] = channel + +# for m in range(1, 5): +# channelName = "{0}-{1}m1".format(name, m) +# channel = PhysicalMarkerChannel(label=channelName) +# channel.sampling_rate = 1.2e9 +# channel.instrument = name +# channel.translator = 'APSPattern' +# self.channels[channelName] = channel + +# mapping = {'digitizerTrig': 'APS1-1m1', +# 'slave_trig': 'APS1-2m1', +# 'q1': 'APS1-12', +# 'M-q1': 'APS1-34', +# 'M-q1-gate': 'APS1-3m1', +# 'q1-gate': 'APS1-4m1', +# 'q2': 'APS2-12', +# 'M-q2': 'APS2-34', +# 'M-q2-gate': 'APS2-1m1', +# 'q2-gate': 'APS2-2m1', +# 'cr': 'APS3-12', +# 'cr-gate': 'APS3-1m1', +# 'M-q1q2': 'APS3-34', +# 'M-q1q2-gate': 'APS3-2m1'} + +# # override trigger lengths on APS1 to get single blips +# self.channels['slave_trig'].pulse_params['length'] = 0.833e-9 +# self.channels['digitizerTrig'].pulse_params['length'] = 0.833e-9 +# self.finalize_map(mapping) + +# def compare_file_data(self, testFile, truthFile): +# ''' +# Override the method in AWGTestHelper so that we can special-case marker comparison +# ''' +# awgData = self.read_function(testFile) +# truthData = self.read_function(truthFile) + +# awgDataLen = len(awgData) +# truthDataLen = len(truthData) + +# self.assertTrue(awgDataLen == truthDataLen, +# "Expected {0} sequences in file. Found {1}.".format( +# truthDataLen, awgDataLen)) + +# for name in truthData: +# self.assertTrue( +# name in awgData, +# "Expected channel {0} not found in file {1}".format(name, +# testFile)) +# isMarker = ('m' == name[-2]) + +# for x in range(len(truthData[name])): +# seqA = np.array(truthData[name][x]) +# seqB = np.array(awgData[name][x]) +# if isMarker: +# self.compare_marker_sequence( +# seqA, seqB, +# "\nFile {0} =>\nChannel {1} Sequence {2}".format( +# testFile, name, x)) +# else: +# self.compare_sequence( +# seqA, seqB, +# "\nFile {0} =>\nChannel {1} Sequence {2}".format( +# testFile, name, x)) + +# def compare_marker_sequence(self, seqA, seqB, errorHeader): +# markerDistanceTolerance = 4 +# self.assertTrue(seqA.size == seqB.size, +# "{0} size {1} != size {2}".format( +# errorHeader, str(seqA.size), str(seqB.size))) + +# # convert sequences to locations of blips +# idxA = np.where(seqA)[0] +# idxB = np.where(seqB)[0] +# self.assertTrue( +# len(idxA) == len(idxB), +# "{0}.\nNumber of blips did not match: {1} != {2}".format( +# errorHeader, len(idxA), len(idxB))) +# # compare the blip locations element-wise +# if len(idxA) > 0: +# diff = np.abs(idxA - idxB) +# else: +# diff = np.array([0]) +# self.assertTrue( +# max(diff) <= markerDistanceTolerance, +# "{0}\nMismatches: {1}".format(errorHeader, diff.nonzero()[0])) + +# @unittest.expectedFailure +# def test_Rabi_RabiWidth(self): +# """ test_Rabi_RabiWidth is expected to fail on APS1 with the following error: +# AssertionError: Oops! You have exceeded the waveform memory of the APS +# """ +# TestSequences.test_Rabi_RabiWidth(self) + +# @unittest.expectedFailure +# def test_RB_SimultaneousRB_AC(self): +# """ test_RB_SimultaneousRB_AC is expected to fail on APS1 with the following error: +# AssertionError: Oops! You have exceeded the waveform memory of the APS +# """ +# TestSequences.test_RB_SimultaneousRB_AC(self) if __name__ == "__main__": unittest.main() From 644555f5725c8026cac52b45a642801168c6d668 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:22:34 -0500 Subject: [PATCH 094/235] Fixed APS and APS2 pattern tests --- tests/test_APS2Pattern.py | 13 +++++-------- tests/test_APSPattern.py | 12 +++++------- 2 files changed, 10 insertions(+), 15 deletions(-) diff --git a/tests/test_APS2Pattern.py b/tests/test_APS2Pattern.py index 636d7843..54dbd1ac 100644 --- a/tests/test_APS2Pattern.py +++ b/tests/test_APS2Pattern.py @@ -9,15 +9,12 @@ class APSPatternUtils(unittest.TestCase): def setUp(self): - self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate') - self.q1 = Qubit(label='q1', gate_chan=self.q1gate) - self.q1 = Qubit(label='q1') + self.cl = ChannelLibrary(db_resource_name=":memory:") + self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase) + self.q1 = self.cl.new_qubit(label='q1') + self.q1.gate_chan = self.q1gate self.q1.pulse_params['length'] = 30e-9 - - ChannelLibrary(blank=True) # Create a blank ChannelLibrary - ChannelLibraries.channelLib.channelDict = {'q1': self.q1, - 'q1-gate': self.q1gate} - ChannelLibraries.channelLib.build_connectivity_graph() + self.cl.update_channelDict() def test_synchronize_control_flow(self): q1 = self.q1 diff --git a/tests/test_APSPattern.py b/tests/test_APSPattern.py index 163f3d21..3bfa2534 100644 --- a/tests/test_APSPattern.py +++ b/tests/test_APSPattern.py @@ -8,14 +8,12 @@ class APSPatternUtils(unittest.TestCase): def setUp(self): - # self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate') - # self.q1 = Qubit(label='q1', gate_chan=self.q1gate) - self.q1 = Qubit(label='q1') + self.cl = ChannelLibrary(db_resource_name=":memory:") + self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase) + self.q1 = self.cl.new_qubit(label='q1') + self.q1.gate_chan = self.q1gate self.q1.pulse_params['length'] = 30e-9 - - ChannelLibrary(blank=True) # Create a blank ChannelLibrary - ChannelLibraries.channelLib.channelDict = {'q1': self.q1} - ChannelLibraries.channelLib.build_connectivity_graph() + self.cl.update_channelDict() def test_unroll_loops_simple(self): q1 = self.q1 From 313947a34891afcc792e88fc8b57b35355c932d4 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:27:22 -0500 Subject: [PATCH 095/235] Fixed test_compiler --- tests/test_Compiler.py | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/tests/test_Compiler.py b/tests/test_Compiler.py index 8c1bd9c7..781bb49c 100644 --- a/tests/test_Compiler.py +++ b/tests/test_Compiler.py @@ -7,23 +7,26 @@ class CompileUtils(unittest.TestCase): def setUp(self): - self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate') - self.q1 = Qubit(label='q1', gate_chan=self.q1gate) + self.cl = ChannelLibrary(db_resource_name=":memory:") + self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase) + self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase) + self.q1phys = Channels.PhysicalChannel(label='q1-phys', sampling_rate=1.2e9, channel_db=self.cl.channelDatabase) + self.q1 = Qubit(label='q1', gate_chan=self.q1gate, channel_db=self.cl.channelDatabase) + self.q1.phys_chan = self.q1phys self.q1.pulse_params['length'] = 30e-9 - self.q2gate = Channels.LogicalMarkerChannel(label='q2-gate') - self.q2 = Qubit(label='q2', gate_chan=self.q2gate) + self.q2gate = Channels.LogicalMarkerChannel(label='q2-gate', channel_db=self.cl.channelDatabase) + self.q2phys = Channels.PhysicalChannel(label='q2-phys', sampling_rate=1.2e9, channel_db=self.cl.channelDatabase) + self.q2 = Qubit(label='q2', gate_chan=self.q2gate, channel_db=self.cl.channelDatabase) + self.q2.phys_chan = self.q2phys self.q2.pulse_params['length'] = 30e-9 - self.trigger = Channels.LogicalMarkerChannel(label='trigger') - self.measq1 = Channels.Measurement(label='M-q1', meas_type='autodyne') + self.trigger = Channels.LogicalMarkerChannel(label='trigger', channel_db=self.cl.channelDatabase) + self.measq1 = Channels.Measurement(label='M-q1', meas_type='autodyne', channel_db=self.cl.channelDatabase) self.measq1.trig_chan = self.trigger - - ChannelLibrary(blank=True) # Create a blank ChannelLibrary - ChannelLibraries.channelLib.channelDict = {'q1': self.q1, - 'q2': self.q2, - 'M-q1': self.measq1} - ChannelLibraries.channelLib.build_connectivity_graph() + self.measq2 = Channels.Measurement(label='M-q2', meas_type='autodyne', channel_db=self.cl.channelDatabase) + self.measq2.trig_chan = self.trigger + self.cl.update_channelDict() def test_add_digitizer_trigger(self): q1 = self.q1 From f36a51f63857ed937b1fdd6c032a28b439182c6f Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:28:33 -0500 Subject: [PATCH 096/235] Destroy YAML --- doc/config/filters.yml | 4 ---- doc/config/instruments.yml | 18 ------------------ doc/config/measure.yml | 16 ---------------- tests/test_measure.yml | 5 ----- 4 files changed, 43 deletions(-) delete mode 100644 doc/config/filters.yml delete mode 100644 doc/config/instruments.yml delete mode 100644 doc/config/measure.yml delete mode 100644 tests/test_measure.yml diff --git a/doc/config/filters.yml b/doc/config/filters.yml deleted file mode 100644 index 546d978d..00000000 --- a/doc/config/filters.yml +++ /dev/null @@ -1,4 +0,0 @@ -q1-RawSS: - type: X6StreamSelector - source: X6-1 - channel: '1' diff --git a/doc/config/instruments.yml b/doc/config/instruments.yml deleted file mode 100644 index 14fd6b6f..00000000 --- a/doc/config/instruments.yml +++ /dev/null @@ -1,18 +0,0 @@ -BBNAPS1: - type: APS2 - tx_channels: - '12': - markers: - 12m1: - delay: -5.0e-08 - -BBNAPS2: - type: APS2 - tx_channels: - '12': - -X6-1: - type: X6 - rx_channels: - '1': - '2': diff --git a/doc/config/measure.yml b/doc/config/measure.yml deleted file mode 100644 index 58327b57..00000000 --- a/doc/config/measure.yml +++ /dev/null @@ -1,16 +0,0 @@ -# Minimal QGL configuration for QGL-demo notebook -# Imports can be made using the !include keyword. e.g. -# instruments: !include instruments.yml -config: - AWGDir: "./awg/" -qubits: - q1: - measure: - AWG: BBNAPS1 12 - trigger: BBNAPS1 12m1 - receiver: q1-RawSS - control: - AWG: BBNAPS2 12 - -instruments: !include instruments.yml -filters: !include filters.yml diff --git a/tests/test_measure.yml b/tests/test_measure.yml deleted file mode 100644 index fd601cca..00000000 --- a/tests/test_measure.yml +++ /dev/null @@ -1,5 +0,0 @@ -config: - AWGDir: /tmp/awg - KernelDir: /tmp/kern - LogDir: /tmp/alog - \ No newline at end of file From 93df6c5328791eb89c68d1974a156b40e6103eeb Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:35:06 -0500 Subject: [PATCH 097/235] Fix test_Scheduler --- tests/helpers.py | 22 +++++++++++++--------- 1 file changed, 13 insertions(+), 9 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index 97f3e7f8..df558eed 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,20 +1,24 @@ from QGL import * def setup_test_lib(): - ChannelLibrary(blank=True) + cl = ChannelLibrary(db_resource_name=":memory:") - q1 = Qubit(label='q1') - q2 = Qubit(label='q2') - q3 = Qubit(label='q3') - q4 = Qubit(label='q4') + q1 = cl.new_qubit(label='q1') + q2 = cl.new_qubit(label='q2') + q3 = cl.new_qubit(label='q3') + q4 = cl.new_qubit(label='q4') + m1 = Measurement(label='M-q1', control_chan=q1, channel_db=cl.channelDatabase) + m2 = Measurement(label='M-q2', control_chan=q2, channel_db=cl.channelDatabase) + m3 = Measurement(label='M-q3', control_chan=q3, channel_db=cl.channelDatabase) + m4 = Measurement(label='M-q4', control_chan=q4, channel_db=cl.channelDatabase) ChannelLibraries.channelLib.channelDict = { 'q1': q1, 'q2': q2, 'q3': q3, 'q4': q4, - 'q1q2': Edge(label='q1q2', source=q1, target=q2), - 'q2q3': Edge(label='q2q3', source=q2, target=q3), - 'q3q4': Edge(label='q3q4', source=q3, target=q4) + 'q1q2': Edge(label='q1q2', source=q1, target=q2, channel_db=cl.channelDatabase), + 'q2q3': Edge(label='q2q3', source=q2, target=q3, channel_db=cl.channelDatabase), + 'q3q4': Edge(label='q3q4', source=q3, target=q4, channel_db=cl.channelDatabase) } - ChannelLibraries.channelLib.build_connectivity_graph() + cl.update_channelDict() From 2b762fb52aa6b99e463c7d7439f69e448c24ca75 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:37:18 -0500 Subject: [PATCH 098/235] Fix test_controlflow --- tests/test_ControlFlow.py | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/tests/test_ControlFlow.py b/tests/test_ControlFlow.py index 2eb40b3f..e526be1f 100644 --- a/tests/test_ControlFlow.py +++ b/tests/test_ControlFlow.py @@ -7,12 +7,17 @@ class ControlFlowTest(unittest.TestCase): def setUp(self): - self.q1 = Qubit(label='q1') - self.q2 = Qubit(label='q2') + cl = ChannelLibrary(db_resource_name=":memory:") + self.q1 = cl.new_qubit(label='q1') + self.q2 = cl.new_qubit(label='q2') + self.q3 = cl.new_qubit(label='q3') + self.q4 = cl.new_qubit(label='q4') + cl.update_channelDict() + # self.q1 = Qubit(label='q1') + # self.q2 = Qubit(label='q2') - ChannelLibrary(blank=True) # Create a blank ChannelLibrary - ChannelLibraries.channelLib.channelDict = {'q1': self.q1, 'q2': self.q2} - ChannelLibraries.channelLib.build_connectivity_graph() + # ChannelLibrary(blank=True) # Create a blank ChannelLibrary + # ChannelLibraries.channelLib.build_connectivity_graph() def test_qif(self): q1 = self.q1 From 36d478f85523b3120aa37899ee773467999b3db0 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:41:30 -0500 Subject: [PATCH 099/235] :tada: ALL UNIT TESTS PASSING :tada: --- tests/test_ControlFlow.py | 7 ------- tests/test_config.py | 3 ++- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/tests/test_ControlFlow.py b/tests/test_ControlFlow.py index e526be1f..a1a9e2a6 100644 --- a/tests/test_ControlFlow.py +++ b/tests/test_ControlFlow.py @@ -13,11 +13,6 @@ def setUp(self): self.q3 = cl.new_qubit(label='q3') self.q4 = cl.new_qubit(label='q4') cl.update_channelDict() - # self.q1 = Qubit(label='q1') - # self.q2 = Qubit(label='q2') - - # ChannelLibrary(blank=True) # Create a blank ChannelLibrary - # ChannelLibraries.channelLib.build_connectivity_graph() def test_qif(self): q1 = self.q1 @@ -25,8 +20,6 @@ def test_qif(self): seq2 = [X(q1), Y(q1), Z(q1)] label(seq1) label(seq2) - # print qif(0, seq1, seq2) - # print ([CmpEq(0), Goto(label(seq1))] + seq2 + [Goto(endlabel(seq1))] + seq1 assert (qif(0, seq1, seq2) == [CmpEq("m", 0), Goto(label(seq1))] + seq2 + [Goto(endlabel(seq1))] + seq1) diff --git a/tests/test_config.py b/tests/test_config.py index 41e5bea3..1513abfe 100644 --- a/tests/test_config.py +++ b/tests/test_config.py @@ -38,6 +38,7 @@ def setUp(self): def tearDown(self): shutil.rmtree('./tmp') + @unittest.skip("Need to update config API") def test_env(self): """ Test that if the BBN_MEAS_FILE environment variable is set, it is used @@ -59,7 +60,7 @@ def test_env(self): assert QGL.config.meas_file == meas_name assert QGL.config.AWGDir == os.path.realpath("./foo/bar/xyz") - + @unittest.skip("Need to update config API") def test_override1(self): """ Tests manually supplying a different config file when instantiating the channel library. From e2233ef971ab860845407169daef34232aa38882 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 21:49:31 -0500 Subject: [PATCH 100/235] Add bbndb to travis.yml --- .travis.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.travis.yml b/.travis.yml index 4258bc7e..53c2289e 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,6 +40,7 @@ install: - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy bokeh h5py jupyter scipy networkx - source activate test-environment - pip install coveralls + - pip install git+https://github.com/BBN-Q/bbndb.git - if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install pygsti; fi script: From 07ee1d28e4cbe1af78eb4a946bcfe3f2ff51687c Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 22:03:30 -0500 Subject: [PATCH 101/235] More travis tweaks --- .travis.yml | 2 +- requirements.txt | 1 + setup.py | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 53c2289e..d90c0b75 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,7 +37,7 @@ install: - conda info -a # Create conda environment with dependencies - - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy bokeh h5py jupyter scipy networkx + - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy bqplot sqlalchemy jupyter scipy networkx - source activate test-environment - pip install coveralls - pip install git+https://github.com/BBN-Q/bbndb.git diff --git a/requirements.txt b/requirements.txt index 187883e3..04eaafe5 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,3 +3,4 @@ numpy >= 1.11.1 scipy >= 0.17.1 networkx >= 1.11 bqplot >= 0.11.5 +sqlalchemy >= 1.2.17 \ No newline at end of file diff --git a/setup.py b/setup.py index 6b6b9261..569e446e 100644 --- a/setup.py +++ b/setup.py @@ -10,4 +10,5 @@ "scipy >= 0.17.1", "networkx >= 1.11", "bqplot >= 0.11.5", + "sqlalchemy >= 1.2.17", ]) From b2de57dd0a79ecb1d1906dd761b10ae4a5701db3 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 22:08:07 -0500 Subject: [PATCH 102/235] More travis tweaks, again --- .travis.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index d90c0b75..942a4b8b 100644 --- a/.travis.yml +++ b/.travis.yml @@ -37,9 +37,10 @@ install: - conda info -a # Create conda environment with dependencies - - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy bqplot sqlalchemy jupyter scipy networkx + - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy sqlalchemy jupyter scipy networkx - source activate test-environment - pip install coveralls + - pip istall bqplot - pip install git+https://github.com/BBN-Q/bbndb.git - if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install pygsti; fi From 2d3752ea4384097be48e536c22ae3f7d2ed1a970 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 12 Feb 2019 22:16:10 -0500 Subject: [PATCH 103/235] More travis tweaks, again, again --- .travis.yml | 2 +- QGL/drivers/APSPattern.py | 23 +++++++++++++++-------- 2 files changed, 16 insertions(+), 9 deletions(-) diff --git a/.travis.yml b/.travis.yml index 942a4b8b..d8a7fa46 100644 --- a/.travis.yml +++ b/.travis.yml @@ -40,7 +40,7 @@ install: - conda create -q -n test-environment python=$TRAVIS_PYTHON_VERSION numpy sqlalchemy jupyter scipy networkx - source activate test-environment - pip install coveralls - - pip istall bqplot + - pip install bqplot - pip install git+https://github.com/BBN-Q/bbndb.git - if [[ $TRAVIS_PYTHON_VERSION == 3.6 ]]; then pip install pygsti; fi diff --git a/QGL/drivers/APSPattern.py b/QGL/drivers/APSPattern.py index d38a0460..120da3a7 100644 --- a/QGL/drivers/APSPattern.py +++ b/QGL/drivers/APSPattern.py @@ -16,7 +16,6 @@ limitations under the License. ''' -import h5py import os import numpy as np from warnings import warn @@ -56,15 +55,12 @@ def get_empty_channel_set(): def get_seq_file_extension(): - return '.h5' - + return '.aps1' def is_compatible_file(filename): - with h5py.File(filename, 'r') as FID: - target = FID['/'].attrs['target hardware'] - if isinstance(target, str): - target = target.encode('utf-8') - if target == b'APS1': + with open(filename, 'rb') as FID: + byte = FID.read(4) + if byte == b'APS1': return True return False @@ -683,6 +679,17 @@ def write_sequence_file(awgData, fileName, miniLLRepeat=1): #Open the HDF5 file if os.path.isfile(fileName): os.remove(fileName) + + + with open(fileName, 'wb') as FID: + FID.write(b'APS1') # target hardware + FID.write(np.float32(2.2).tobytes()) # Version + FID.write(np.float32(4.0).tobytes()) # minimum firmware version + FID.write(np.uint16(2).tobytes()) # number of channels + FID.write(np.uint16([1, 2]).tobytes()) # channelDataFor + FID.write(np.uint64(instructions.size).tobytes()) # instructions length + FID.write(instructions.tobytes()) # instructions in uint64 form + with h5py.File(fileName, 'w') as FID: #List of which channels we have data for From 25b694b4bf15daf9123ba0ec51dd39e82d9f2a70 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 13 Feb 2019 15:30:13 -0500 Subject: [PATCH 104/235] Added h5->aps binary conversion utility. Working on APS1 binary format --- .gitattributes | 1 + QGL/drivers/APSPattern.py | 79 +++++++++++++++++------------ utils/convert_h5_to_aps.py | 101 +++++++++++++++++++++++++++++++++++++ 3 files changed, 148 insertions(+), 33 deletions(-) create mode 100644 utils/convert_h5_to_aps.py diff --git a/.gitattributes b/.gitattributes index 3477558f..201c7c94 100644 --- a/.gitattributes +++ b/.gitattributes @@ -1,2 +1,3 @@ *.h5 filter=lfs diff=lfs merge=lfs -text *.aps2 filter=lfs diff=lfs merge=lfs -text +*.aps1 filter=lfs diff=lfs merge=lfs -text diff --git a/QGL/drivers/APSPattern.py b/QGL/drivers/APSPattern.py index 120da3a7..226a67a4 100644 --- a/QGL/drivers/APSPattern.py +++ b/QGL/drivers/APSPattern.py @@ -680,26 +680,20 @@ def write_sequence_file(awgData, fileName, miniLLRepeat=1): if os.path.isfile(fileName): os.remove(fileName) - + print("WRiting ", fileName) with open(fileName, 'wb') as FID: + channelDataFor = np.array([0,0,0,0], dtype=np.bool) + if LLs12: + channelDataFor[0:2] = True + if LLs34: + channelDataFor[2:] = True + LLData = [LLs12, LLs34] + repeats = [0, 0] + FID.write(b'APS1') # target hardware FID.write(np.float32(2.2).tobytes()) # Version - FID.write(np.float32(4.0).tobytes()) # minimum firmware version - FID.write(np.uint16(2).tobytes()) # number of channels - FID.write(np.uint16([1, 2]).tobytes()) # channelDataFor - FID.write(np.uint64(instructions.size).tobytes()) # instructions length - FID.write(instructions.tobytes()) # instructions in uint64 form - - with h5py.File(fileName, 'w') as FID: - - #List of which channels we have data for - #TODO: actually handle incomplete channel data - channelDataFor = [1, 2] if LLs12 else [] - channelDataFor += [3, 4] if LLs34 else [] - FID['/'].attrs['Version'] = 2.2 - FID['/'].attrs['target hardware'] = 'APS1' - FID['/'].attrs['channelDataFor'] = np.uint16(channelDataFor) - FID['/'].attrs['miniLLRepeat'] = np.uint16(miniLLRepeat - 1) + FID.write(channelDataFor.tobytes()) # channelDataFor + FID.write(np.array(miniLLRepeat-1, dtype=np.bool).tobytes()) # MiniLLRepeat #Create the waveform vectors wfInfo = [] @@ -709,29 +703,39 @@ def write_sequence_file(awgData, fileName, miniLLRepeat=1): wfInfo.append(create_wf_vector({key: wf.imag for key, wf in wfLib.items()})) - LLData = [LLs12, LLs34] - repeats = [0, 0] #Create the groups and datasets for chanct in range(4): - chanStr = '/chan_{0}'.format(chanct + 1) - chanGroup = FID.create_group(chanStr) - chanGroup.attrs['isIQMode'] = np.uint8(1) - #Write the waveformLib to file - FID.create_dataset('{0}/waveformLib'.format(chanStr), - data=wfInfo[chanct][0]) + # chanStr = '/chan_{0}'.format(chanct + 1) + # chanGroup = FID.create_group(chanStr) + FID.write(np.uint8(1).tobytes()) # isIQMode + FID.write(np.uint64(wfInfo[chanct][0].size).tobytes()) # Length of waveforms + FID.write(wfInfo[chanct][0].tobytes()) # Waveforms np.int16 + # chanGroup.attrs['isIQMode'] = np.uint8(1) + #Write the waveformLib to file + # FID.create_dataset('{0}/waveformLib'.format(chanStr), + # data=wfInfo[chanct][0]) + for chanct in [1,3]: #For A channels (1 & 3) we write link list data if we actually have any - if (np.mod(chanct, 2) == 0) and LLData[chanct // 2]: - groupStr = chanStr + '/linkListData' - LLGroup = FID.create_group(groupStr) + if LLData[chanct // 2]: + # groupStr = chanStr + '/linkListData' + # LLGroup = FID.create_group(groupStr) LLDataVecs, numEntries = create_LL_data( LLData[chanct // 2], wfInfo[chanct][1], os.path.basename(fileName)) - LLGroup.attrs['length'] = numEntries + # LLGroup.attrs['length'] = numEntries + FID.write(np.uint64(len(LLDataVecs.keys())).tobytes()) # numKeys + FID.write(np.uint64(numEntries).tobytes()) # numEntries + + # print(numEntries, len(LLDataVecs.keys())) for key, dataVec in LLDataVecs.items(): - FID.create_dataset(groupStr + '/' + key, data=dataVec) - else: - chanGroup.attrs['isLinkListData'] = np.uint8(0) + print(key, dataVec.dtype) + FID.write(key.ljust(32,"#").encode("utf-8")) # Key 32 byte utf-8 + FID.write(dataVec.tobytes()) # Data np.uint16 + # FID.create_dataset(groupStr + '/' + key, data=dataVec) + + # else: + # chanGroup.attrs['isLinkListData'] = np.uint8(0) def read_sequence_file(fileName): @@ -749,6 +753,15 @@ def read_sequence_file(fileName): chanStrs2 = ['chan_1', 'chan_2', 'chan_3', 'chan_4'] mrkStrs = ['ch1m1', 'ch2m1', 'ch3m1', 'ch4m1'] + with open(fileName, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + file_version = struct.unpack(' Date: Wed, 13 Feb 2019 21:51:12 -0500 Subject: [PATCH 105/235] Migrating all test data to binary format --- QGL/drivers/TekPattern.py | 405 ------------------ .../awg/TestAPS1/AllXY/AllXY-APS1.aps1 | 3 + .../awg/TestAPS1/CPMG/CPMG-APS1.aps1 | 3 + .../awg/TestAPS1/Echo/Echo-APS1.aps1 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS1.aps1 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS2.aps1 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS3.aps1 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS1.aps1 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS2.aps1 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS3.aps1 | 3 + .../awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 | 3 + .../awg/TestAPS1/MISC1/MISC1-APS1.aps1 | 3 + .../awg/TestAPS1/MISC2/MISC2-APS1.aps1 | 3 + .../awg/TestAPS1/MISC2/MISC2-APS3.aps1 | 3 + .../awg/TestAPS1/MISC3/MISC3-APS1.aps1 | 3 + .../awg/TestAPS1/MISC3/MISC3-APS2.aps1 | 3 + .../awg/TestAPS1/MISC3/MISC3-APS3.aps1 | 3 + .../awg/TestAPS1/MISC4/MISC4-APS1.aps1 | 3 + .../awg/TestAPS1/MISC4/MISC4-APS2.aps1 | 3 + .../awg/TestAPS1/MISC4/MISC4-APS3.aps1 | 3 + .../awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 | 3 + .../awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 | 3 + .../awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 | 3 + .../awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 | 3 + .../awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 | 3 + .../awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 | 3 + .../TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 | 3 + .../TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 | 3 + .../TestAPS1/RabiWidth/Rabi/Rabi-APS1.aps1 | 3 + .../awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 | 3 + .../awg/TestAPS1/SPAM/SPAM-APS1.aps1 | 3 + .../SimultaneousRB_AC/RB/RB-APS1.aps1 | 3 + .../SimultaneousRB_AC/RB/RB-APS2.aps1 | 3 + .../TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 | 3 + .../TestAPS1/SingleShot/SingleShot-APS1.aps1 | 3 + .../awg/TestAPS1/Spec/Spec-APS1.aps1 | 3 + tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 | 3 + .../awg/TestAPS2/AllXY/AllXY-APS1.aps2 | 3 + .../awg/TestAPS2/AllXY/AllXY-APS2.aps2 | 3 + .../CNOT_CR_mux/CNOT_CR_mux-APS1.aps2 | 3 + .../CNOT_CR_mux/CNOT_CR_mux-APS3.aps2 | 3 + .../CNOT_CR_mux/CNOT_CR_mux-APS5.aps2 | 3 + .../awg/TestAPS2/CPMG/CPMG-APS1.aps2 | 3 + .../awg/TestAPS2/CPMG/CPMG-APS2.aps2 | 3 + .../awg/TestAPS2/Echo/Echo-APS1.aps2 | 3 + .../awg/TestAPS2/Echo/Echo-APS2.aps2 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS1.aps2 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS2.aps2 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS3.aps2 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS4.aps2 | 3 + .../EchoCRLen/EchoCR/EchoCR-APS5.aps2 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS1.aps2 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS2.aps2 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS3.aps2 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS4.aps2 | 3 + .../EchoCRPhase/EchoCR/EchoCR-APS5.aps2 | 3 + .../awg/TestAPS2/FlipFlop/FlipFlop-APS1.aps2 | 3 + .../awg/TestAPS2/FlipFlop/FlipFlop-APS2.aps2 | 3 + .../awg/TestAPS2/MISC1/MISC1-APS1.aps2 | 3 + .../awg/TestAPS2/MISC2/MISC2-APS1.aps2 | 3 + .../awg/TestAPS2/MISC2/MISC2-APS5.aps2 | 3 + .../awg/TestAPS2/MISC3/MISC3-APS1.aps2 | 3 + .../awg/TestAPS2/MISC3/MISC3-APS3.aps2 | 3 + .../awg/TestAPS2/MISC3/MISC3-APS5.aps2 | 3 + .../awg/TestAPS2/MISC4/MISC4-APS1.aps2 | 3 + .../awg/TestAPS2/MISC4/MISC4-APS3.aps2 | 3 + .../awg/TestAPS2/MISC4/MISC4-APS5.aps2 | 3 + .../awg/TestAPS2/PiRabi/PiRabi-APS1.aps2 | 3 + .../awg/TestAPS2/PiRabi/PiRabi-APS2.aps2 | 3 + .../awg/TestAPS2/PiRabi/PiRabi-APS3.aps2 | 3 + .../awg/TestAPS2/PiRabi/PiRabi-APS4.aps2 | 3 + .../awg/TestAPS2/PiRabi/PiRabi-APS5.aps2 | 3 + .../awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.aps2 | 3 + .../awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.aps2 | 3 + .../awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.aps2 | 3 + .../awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.aps2 | 3 + .../awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.aps2 | 3 + .../awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.aps2 | 3 + .../TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.aps2 | 3 + .../TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.aps2 | 3 + .../TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.aps2 | 3 + .../TestAPS2/RabiWidth/Rabi/Rabi-APS1.aps2 | 3 + .../TestAPS2/RabiWidth/Rabi/Rabi-APS2.aps2 | 3 + .../awg/TestAPS2/Ramsey/Ramsey-APS1.aps2 | 3 + .../awg/TestAPS2/Ramsey/Ramsey-APS2.aps2 | 3 + .../awg/TestAPS2/SPAM/SPAM-APS1.aps2 | 3 + .../awg/TestAPS2/SPAM/SPAM-APS2.aps2 | 3 + .../SimultaneousRB_AC/RB/RB-APS1.aps2 | 3 + .../SimultaneousRB_AC/RB/RB-APS2.aps2 | 3 + .../SimultaneousRB_AC/RB/RB-APS3.aps2 | 3 + .../SimultaneousRB_AC/RB/RB-APS4.aps2 | 3 + .../TestAPS2/SingleQubitRB/RB/RB-APS1.aps2 | 3 + .../TestAPS2/SingleQubitRB/RB/RB-APS2.aps2 | 3 + .../TestAPS2/SingleShot/SingleShot-APS1.aps2 | 3 + .../TestAPS2/SingleShot/SingleShot-APS2.aps2 | 3 + .../awg/TestAPS2/Spec/Spec-APS1.aps2 | 3 + .../awg/TestAPS2/Spec/Spec-APS2.aps2 | 3 + tests/test_data/awg/TestAPS2/T1/T1-APS1.aps2 | 3 + tests/test_data/awg/TestAPS2/T1/T1-APS2.aps2 | 3 + tests/test_data/multi-align.aps2 | 3 + tests/test_data/multi-composite.aps2 | 3 + tests/test_data/multi-operators.aps2 | 3 + tests/test_data/single-ramsey.aps2 | 3 + tests/test_data/single-repeat.aps2 | 3 + 104 files changed, 309 insertions(+), 405 deletions(-) delete mode 100644 QGL/drivers/TekPattern.py create mode 100644 tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 create mode 100644 tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 create mode 100644 tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 create mode 100644 tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 create mode 100644 tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.aps1 create mode 100644 tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 create mode 100644 tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/Echo/Echo-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/Echo/Echo-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.aps2 create mode 100644 tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.aps2 create mode 100644 tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/Spec/Spec-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/Spec/Spec-APS2.aps2 create mode 100644 tests/test_data/awg/TestAPS2/T1/T1-APS1.aps2 create mode 100644 tests/test_data/awg/TestAPS2/T1/T1-APS2.aps2 create mode 100644 tests/test_data/multi-align.aps2 create mode 100644 tests/test_data/multi-composite.aps2 create mode 100644 tests/test_data/multi-operators.aps2 create mode 100644 tests/test_data/single-ramsey.aps2 create mode 100644 tests/test_data/single-repeat.aps2 diff --git a/QGL/drivers/TekPattern.py b/QGL/drivers/TekPattern.py deleted file mode 100644 index 11464977..00000000 --- a/QGL/drivers/TekPattern.py +++ /dev/null @@ -1,405 +0,0 @@ -""" -TekPattern for creating Tek AWG files. Based off of the Matlab version. - -Created on Tue May 8 20:39:26 2012 - -Copyright 2013 Raytheon BBN Technologies - -Licensed under the Apache License, Version 2.0 (the "License"); -you may not use this file except in compliance with the License. -You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - -Unless required by applicable law or agreed to in writing, software -distributed under the License is distributed on an "AS IS" BASIS, -WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -See the License for the specific language governing permissions and -limitations under the License. -""" - -import struct -import io -import h5py -import numpy as np -import re - -MAX_WAVEFORM_VALUE = 2**13 - 1 #maximum waveform value i.e. 14bit DAC - -# Do we want a pulse file per instrument or per channel -SEQFILE_PER_CHANNEL = False - -def get_empty_channel_set(): - return {'ch12': {}, - 'ch34': {}, - 'ch1m1': {}, - 'ch1m2': {}, - 'ch2m1': {}, - 'ch2m2': {}, - 'ch3m1': {}, - 'ch3m2': {}, - 'ch4m1': {}, - 'ch4m2': {}} - - -def get_seq_file_extension(): - return '.awg' - - -def is_compatible_file(filename): - import os - if os.path.splitext(filename)[1] == get_seq_file_extension(): - return True - else: - return False - - -def write_field(FID, fieldName, data, dataType): - typeSizes = {'int16': 2, 'int32': 4, 'double': 8, 'uint128': 16} - formatChars = {'int16': ' 1] = 1.0 - analog[analog < -1] = -1.0 - - maxLength = max(analog.size, marker1.size, marker2.size) - - if marker1.size < maxLength: - marker1 = np.append(marker1, - np.zeros(maxLength - marker1.size, - dtype=np.bool)) - if marker2.size < maxLength: - marker2 = np.append(marker2, - np.zeros(maxLength - marker2.size, - dtype=np.bool)) - if analog.size < maxLength: - analog = np.append(analog, - np.zeros(maxLength - analog.size, - dtype=np.float64)) - - binData = np.uint16(MAX_WAVEFORM_VALUE * analog + MAX_WAVEFORM_VALUE) - binData += 2**14 * np.uint16(marker1) + 2**15 * np.uint16(marker2) - - return binData - - -def write_waveform(FID, WFname, WFnumber, data): - ''' - Helper function to write a waveform - ''' - numString = str(WFnumber) - - write_field(FID, 'WAVEFORM_NAME_' + numString, WFname, 'char') - - #Set integer format - write_field(FID, 'WAVEFORM_TYPE_' + numString, 1, 'int16') - - write_field(FID, 'WAVEFORM_LENGTH_' + numString, data.size, 'int32') - - write_field(FID, 'WAVEFORM_TIMESTAMP_' + numString, 0, 'uint128') - tmpString = 'WAVEFORM_DATA_' + numString + chr(0) - dataSize = 2 * data.size - FID.write(struct.pack('> 14 - wfData[name + 'm2'] = (wf & marker2Mask) >> 15 - elif nameRE.match(name): - wfNum = nameRE.findall(name)[0] - waveformNames[data] = 'WAVEFORM_DATA_' + wfNum - elif seqNameRE.match(name): - channel, seqNum = seqNameRE.findall(name)[0] - if not ('ch' + channel) in seqTable: - seqTable['ch' + channel] = {int(seqNum): data} - else: - seqTable['ch' + channel][int(seqNum)] = data - - # check if there is more to read - b = FID.read(1) - if b == '': - break - else: - FID.seek(-1, 1) - - # now that we have everything, loop through seqTable to build AWGData - AWGData = {ch: [] for ch in seqTable.keys()} - # add in marker keys - for ch in seqTable.keys(): - AWGData[ch + 'm1'] = [] - AWGData[ch + 'm2'] = [] - - seqLength = max([int(x) for x in seqTable['ch1'].keys()]) - - for channel in seqTable.keys(): - for seqct in range(seqLength): - seqEntry = seqTable[channel][seqct + 1] - wfName = waveformNames[seqEntry] - AWGData[channel].append(wfData[wfName]) - AWGData[channel + 'm1'].append(wfData[wfName + 'm1']) - AWGData[channel + 'm2'].append(wfData[wfName + 'm2']) - - return AWGData - - -if __name__ == '__main__': - - pass diff --git a/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 b/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 new file mode 100644 index 00000000..7fba53b0 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c79184b0806766b42dc92f873beea634e2292d60edda5be14065eaa519206f5a +size 3989 diff --git a/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 b/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 new file mode 100644 index 00000000..9d9b903c --- /dev/null +++ b/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:492602ef1ac6e451f6b7f531470af0f23b31d69fd2d2d2e0e1ea1bbd770eaf6d +size 2355 diff --git a/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 b/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 new file mode 100644 index 00000000..bf09543a --- /dev/null +++ b/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:85cc8c04c27d1dacdec56d3942a65ac268da07a523226bfd57607e18e587d532 +size 2925 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 new file mode 100644 index 00000000..2ae5e8b8 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fb5139183372cc3e055c69590c7556c933db502ed2333c4d7856efdc2f67462 +size 4207 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 new file mode 100644 index 00000000..057f5af0 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:48193962f6abc6b1d8c0b4242c8c260e948fc9e7ed706d4971702277cc943698 +size 3147 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 new file mode 100644 index 00000000..590ec1bc --- /dev/null +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2db9971c60a9df840a9443e5a1d381db92d9da9d818ad213b527efe0f179ef89 +size 3367 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 new file mode 100644 index 00000000..eaed969e --- /dev/null +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:979b141165b4d0a5292329a0cbaeb18dea7de66c1670bb8da99b22c42ba9016f +size 4207 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 new file mode 100644 index 00000000..80486dc5 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2516fbc0a51c9fbf54c1c3126911e76bee7c1cf3003ee743c82c8ff35cf99edc +size 3511 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 new file mode 100644 index 00000000..fd1dcc19 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cc3fc6bde52e7723effe78eb35d02a21e2721dc66d6e825435d6ec9937cb7bf7 +size 11407 diff --git a/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 b/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 new file mode 100644 index 00000000..a243b583 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1d1157dea39f1b1c023983268a96f87b90fc364875120605f6a1c2bd2b66f12e +size 23529 diff --git a/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 new file mode 100644 index 00000000..ff116d24 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6aec33015183570c3f23149df34361064ec81158c5be7b6dfa42c427c9f2b003 +size 3841 diff --git a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 new file mode 100644 index 00000000..15b31644 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9141cca1496506a615c568dbdf36a741c6ed953ff49773c1a0b0444566d31541 +size 657 diff --git a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 new file mode 100644 index 00000000..048d8dde --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:54b3fd28c21e1dea5b7367782a2c5cecdd66407e5566b01c5c3dd3c958d98d1e +size 753 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 new file mode 100644 index 00000000..b87471d5 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8686964ef675b7ddfe0d038a823237e8d8a02a79169fbf1d5f946f7a2347a8ee +size 683 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 new file mode 100644 index 00000000..4f15b5ef --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e33b8bfca6d77c3f4db6de021f3aaddf38d2f0ef5b31b0c005c173a2c275385a +size 441 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 new file mode 100644 index 00000000..a5ca693b --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f8d6633b20b4405ae56a260a36874486a816b734e8f479eef8aab7d4522c34e5 +size 753 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 new file mode 100644 index 00000000..e4a0ba9a --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d1182d7ee93b7e7990b518b42c85b8483247203922c831c3fb386ced529d0a5 +size 1149 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 new file mode 100644 index 00000000..7c0d364c --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:cf1666a6e70851c52a0f27240300cf620462539c63df9b5ec8d9f022c4d9ce42 +size 903 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 new file mode 100644 index 00000000..a7c51ea4 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b6e7be64402e6eee1152e294b53228eccdb37840fb243e68e452776951dcb8de +size 773 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 new file mode 100644 index 00000000..f34a039b --- /dev/null +++ b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:4c14c23f7b2e24985a2a800b16e3786c8985470aa440a97c972ff8f88554d435 +size 2997 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 new file mode 100644 index 00000000..c61a61e5 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ff2b0e15651317fdb1dcb320a73d56954f00770eb866ce6843cb3d18b018d65c +size 2377 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 new file mode 100644 index 00000000..76341295 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9830bdd5c69c8ddb7e0656e78c4a06b61b205a5de7e7fb620ae7c1f423d79667 +size 1887 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 new file mode 100644 index 00000000..0066ecad --- /dev/null +++ b/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c844118df061ae11cbf8f5e66ccad2b51b3d838181b0e3ccb2d006cd386b98e +size 2903 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 new file mode 100644 index 00000000..0066ecad --- /dev/null +++ b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0c844118df061ae11cbf8f5e66ccad2b51b3d838181b0e3ccb2d006cd386b98e +size 2903 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 new file mode 100644 index 00000000..459987f9 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:55ccffcdd6ff26268d185054506ebc65fb9e67eded775011073d9f77922deb94 +size 2803 diff --git a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 new file mode 100644 index 00000000..df3425fa --- /dev/null +++ b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8e2d323573f37158d606c7c0f87320f35749da11899da5758898df83216a1074 +size 2423 diff --git a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 new file mode 100644 index 00000000..146d1267 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab58f521b56d072a3756047531e2397e0f48ae175465c4d4903f2b404dbd055d +size 1837 diff --git a/tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.aps1 new file mode 100644 index 00000000..afc9515c --- /dev/null +++ b/tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30c8ee6a5193280bdc158fdd762c7c05a744dc78045dc9100286b3dd501f9e24 +size 13 diff --git a/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 b/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 new file mode 100644 index 00000000..bb82c73d --- /dev/null +++ b/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:773ff3534bb9c217c3753d59126dadf35d736f6e8785756896756a3d316605da +size 2361 diff --git a/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 b/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 new file mode 100644 index 00000000..26b8f04c --- /dev/null +++ b/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8acf4b023fd7a4e2e7c983f7ebd2fd9eda870d5ffb5f2ef2ac37adb6fed1f2c6 +size 39819 diff --git a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.aps1 b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.aps1 new file mode 100644 index 00000000..afc9515c --- /dev/null +++ b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30c8ee6a5193280bdc158fdd762c7c05a744dc78045dc9100286b3dd501f9e24 +size 13 diff --git a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.aps1 b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.aps1 new file mode 100644 index 00000000..afc9515c --- /dev/null +++ b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30c8ee6a5193280bdc158fdd762c7c05a744dc78045dc9100286b3dd501f9e24 +size 13 diff --git a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 b/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 new file mode 100644 index 00000000..fde6c83e --- /dev/null +++ b/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:07eddb3d9ee5da4bfada4c4bb18f2a0f3222475932c9a3a2c82a406e2238ebe9 +size 121305 diff --git a/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 b/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 new file mode 100644 index 00000000..9ecd230b --- /dev/null +++ b/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:93807ccf7f1e8b14170513bf895265808db74c490226e32e94d10b18c1ec9822 +size 1147 diff --git a/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 b/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 new file mode 100644 index 00000000..da311979 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:33985607d236dccec3424242fbfc3b21d51d64ed5037750565f0c89f671b2add +size 1107 diff --git a/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 b/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 new file mode 100644 index 00000000..cae5aca6 --- /dev/null +++ b/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:703d52f368fe7062f2aedd7bd5d2d2fc4b672971b75865a626e4384c0ebbcb68 +size 1907 diff --git a/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.aps2 b/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.aps2 new file mode 100644 index 00000000..c4cf5611 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84c8e5cc7c128b976fb19122933276d9a1263524fd17a016764d10b3992480f4 +size 5550 diff --git a/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.aps2 b/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.aps2 new file mode 100644 index 00000000..b7b1c359 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/AllXY/AllXY-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f601221c8a1df53d6defc97aba63c77780e9b97d69726c6a3b0b9ef987e2cb89 +size 2894 diff --git a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.aps2 b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.aps2 new file mode 100644 index 00000000..c57aa3f2 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2f847ac93b35b559fbe0afab344c8d23ef064b4ac5c2bc9612b6723cd680e1ff +size 854 diff --git a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.aps2 b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.aps2 new file mode 100644 index 00000000..2d360388 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2127036b39a846267d5a4cd8b5659abed0d8bcd22b200acbaf0f30265e23d2d +size 278 diff --git a/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.aps2 b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.aps2 new file mode 100644 index 00000000..148df35e --- /dev/null +++ b/tests/test_data/awg/TestAPS2/CNOT_CR_mux/CNOT_CR_mux-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:142a223f060a567f1fc94f06817e41256779fea86bd04cf006a000a284cfb680 +size 98 diff --git a/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.aps2 b/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.aps2 new file mode 100644 index 00000000..87cf1736 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3be613e095ae800922fe93d3d1511cbba01fa2a9955578c53d7dcbaf68c8e012 +size 1982 diff --git a/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.aps2 b/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.aps2 new file mode 100644 index 00000000..11ea6c5c --- /dev/null +++ b/tests/test_data/awg/TestAPS2/CPMG/CPMG-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ab7bdc5ad53a54bf0ef6eb859d746255df47be061489b93e3c5a733060085425 +size 1246 diff --git a/tests/test_data/awg/TestAPS2/Echo/Echo-APS1.aps2 b/tests/test_data/awg/TestAPS2/Echo/Echo-APS1.aps2 new file mode 100644 index 00000000..7ef6e389 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/Echo/Echo-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d8777be258150c69db56d7bf5446bdbf07b44eab9d1cd07ca85afb609c2f4677 +size 2886 diff --git a/tests/test_data/awg/TestAPS2/Echo/Echo-APS2.aps2 b/tests/test_data/awg/TestAPS2/Echo/Echo-APS2.aps2 new file mode 100644 index 00000000..d5e38136 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/Echo/Echo-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:b87d3f879174b7af4502443943bc099eb69a9a559df4ba618cd52b084ae0d9ae +size 1598 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.aps2 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.aps2 new file mode 100644 index 00000000..f9ce9f77 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27882f98f2386e02b8f7bab8dc0cbbc9c70f8892a621e8f07d550f87392eaf8e +size 4310 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.aps2 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.aps2 new file mode 100644 index 00000000..5ff1fa82 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27544612cfc2a8f7e8b9b1306672e5f9ecf16b8b22712de3482797da6417ea3b +size 2862 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.aps2 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.aps2 new file mode 100644 index 00000000..bad77e86 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:094c46db9912e1a4a1c2e5fd3f4e8affb52ff59b84e17b042a75d6dfc4c86f7d +size 2318 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.aps2 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.aps2 new file mode 100644 index 00000000..5ff1fa82 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS4.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:27544612cfc2a8f7e8b9b1306672e5f9ecf16b8b22712de3482797da6417ea3b +size 2862 diff --git a/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.aps2 b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.aps2 new file mode 100644 index 00000000..1957c297 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRLen/EchoCR/EchoCR-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c596ac8bb53ceb5afbaaa2a76eeec682298d26e4e11aca1694d060fdaffbe507 +size 5710 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.aps2 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.aps2 new file mode 100644 index 00000000..c14e60e7 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f5c00e601b1472df2167f948d585a951e4c8f369785db23f91a94ea9a8de7ed4 +size 4310 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.aps2 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.aps2 new file mode 100644 index 00000000..1b50f181 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8989adb01d5d1b23c36bebcfa6707a1000fab28057cec9f6ddeb3d385c0cfb70 +size 2862 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.aps2 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.aps2 new file mode 100644 index 00000000..3f26f413 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:67e54be6fc7da5fea7b15ee46bdeb84aa97483cd1f5da827f298be78a75e28e6 +size 2814 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.aps2 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.aps2 new file mode 100644 index 00000000..1b50f181 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS4.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8989adb01d5d1b23c36bebcfa6707a1000fab28057cec9f6ddeb3d385c0cfb70 +size 2862 diff --git a/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.aps2 b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.aps2 new file mode 100644 index 00000000..c2d1bbd1 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/EchoCRPhase/EchoCR/EchoCR-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:998ed2c3fb7c85f69823c69e6ce567ead8a65003fe9df78e4d4891bd95a89493 +size 13758 diff --git a/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.aps2 b/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.aps2 new file mode 100644 index 00000000..591b20d6 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:16b1ab91da85f6996eb88a4f480dc646247cf284a1cf2c6f68cfc171b1c11cbb +size 27566 diff --git a/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.aps2 b/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.aps2 new file mode 100644 index 00000000..5ceec56e --- /dev/null +++ b/tests/test_data/awg/TestAPS2/FlipFlop/FlipFlop-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:d1fa32e761c559b4d4f286bbb5a1203c5cc4b18554d4d72282716649eb68330c +size 15198 diff --git a/tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.aps2 b/tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.aps2 new file mode 100644 index 00000000..cbf62bbb --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC1/MISC1-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb695b5795e4c1ab729d4800d939687489312e675b8bbb2bdde34d129d15a2f7 +size 3566 diff --git a/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.aps2 b/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.aps2 new file mode 100644 index 00000000..d7d18a41 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6c9c042af3d9890243f33df8c82ad8858dca532e86115b33dbf065e8ecd6df7a +size 302 diff --git a/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.aps2 b/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.aps2 new file mode 100644 index 00000000..73c2d580 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC2/MISC2-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:9d8f4baee00287d69c030d07c58b55dabc45d4d4a68028c0b517c95c7f67ceae +size 638 diff --git a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.aps2 b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.aps2 new file mode 100644 index 00000000..05147f69 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:2648062292b4b86379641948f42f9b3e580f061e646ebc719518fae5ae2b47b5 +size 366 diff --git a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.aps2 b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.aps2 new file mode 100644 index 00000000..2d360388 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2127036b39a846267d5a4cd8b5659abed0d8bcd22b200acbaf0f30265e23d2d +size 278 diff --git a/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.aps2 b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.aps2 new file mode 100644 index 00000000..9e74a68a --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC3/MISC3-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a2e3ecdb003db075586c2a206bc7d7682b1ea64e298934c3f9d4fe5e80df989b +size 654 diff --git a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.aps2 b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.aps2 new file mode 100644 index 00000000..7e82c465 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:afc0ad90fef6b1152038b523a4d9e60ebe07b4b873118f5af886299d0c4084b3 +size 542 diff --git a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.aps2 b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.aps2 new file mode 100644 index 00000000..9349b597 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:30ae36b2e96aff26f55a71282a6e3c5dab02439e39c0ace27add0eea05383224 +size 790 diff --git a/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.aps2 b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.aps2 new file mode 100644 index 00000000..e0e78833 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/MISC4/MISC4-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c561c0e4b1bfbe9f1cbb503eb755a0323b5be68ff3e7a145edfa27a324ed2a8 +size 694 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.aps2 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.aps2 new file mode 100644 index 00000000..058335ff --- /dev/null +++ b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5db547eede6bb82dedff31595a5f103867edd64487509451e40be0f47fcc5431 +size 3166 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.aps2 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.aps2 new file mode 100644 index 00000000..30caceef --- /dev/null +++ b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5077d62fe51b3fad882dbd73f5783eb4aa0a9f8dcd2dd43d31d518878a95fc87 +size 2246 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.aps2 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.aps2 new file mode 100644 index 00000000..6aa6816e --- /dev/null +++ b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:365c689f3611c49bae72fde4db099063f1b6bcc6fc90b12bacef100f21f53647 +size 1702 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.aps2 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.aps2 new file mode 100644 index 00000000..30caceef --- /dev/null +++ b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS4.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5077d62fe51b3fad882dbd73f5783eb4aa0a9f8dcd2dd43d31d518878a95fc87 +size 2246 diff --git a/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.aps2 b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.aps2 new file mode 100644 index 00000000..3577086d --- /dev/null +++ b/tests/test_data/awg/TestAPS2/PiRabi/PiRabi-APS5.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:a005b4fab6c710c68cd3fa008ac4f25d4f8b4358f4d65f97a4cb4726e1098d32 +size 2614 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.aps2 b/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.aps2 new file mode 100644 index 00000000..e7d23b5e --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84c2f406fc1765013f9c203a8e6eaffc97cfc228a6bbb4a1ad235b7d55de2e63 +size 2590 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.aps2 b/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.aps2 new file mode 100644 index 00000000..e9188c2c --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmp/Rabi/Rabi-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:959f0c471be1bcf3fd7177b80184f464afa2657c80a29892f10ac62eb50754fa +size 1070 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.aps2 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.aps2 new file mode 100644 index 00000000..e7d23b5e --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:84c2f406fc1765013f9c203a8e6eaffc97cfc228a6bbb4a1ad235b7d55de2e63 +size 2590 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.aps2 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.aps2 new file mode 100644 index 00000000..e9188c2c --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:959f0c471be1bcf3fd7177b80184f464afa2657c80a29892f10ac62eb50754fa +size 1070 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.aps2 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.aps2 new file mode 100644 index 00000000..a4b21884 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:f0a75b4f3a1ac4fb541d65a89e1050ddf2d5b3827b3ef1d50d6d6a7daf0bde17 +size 2150 diff --git a/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.aps2 b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.aps2 new file mode 100644 index 00000000..e9188c2c --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmp2/Rabi/Rabi-APS4.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:959f0c471be1bcf3fd7177b80184f464afa2657c80a29892f10ac62eb50754fa +size 1070 diff --git a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.aps2 b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.aps2 new file mode 100644 index 00000000..f3e01bba --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:6dd034f72cef034a68543bb4739f3748da2b8b978c2e8532e862c6cb4bb6ebbb +size 2718 diff --git a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.aps2 b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.aps2 new file mode 100644 index 00000000..1c87b609 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8d47634aa2cc2809834475a0f117e9d9d828e85bca3b24e582affdc224a7b2f1 +size 1086 diff --git a/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.aps2 b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.aps2 new file mode 100644 index 00000000..31bd854f --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiAmpPi/Rabi/Rabi-APS4.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:8fa84799426b4229cc3b14d4ee352d7e8086d1b4364ff12247302d7ae63f3124 +size 1246 diff --git a/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.aps2 b/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.aps2 new file mode 100644 index 00000000..10040774 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:1760a6778f5be4e1ed2ff400754eed899bbfcb2eaf023ba549888d2f1761377c +size 133022 diff --git a/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.aps2 b/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.aps2 new file mode 100644 index 00000000..9ec47b44 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/RabiWidth/Rabi/Rabi-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:63a70eb6ace79b1e32d3c4c84e8289ac6ff3bcf03b3af2a98b72becf1788497d +size 1070 diff --git a/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.aps2 b/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.aps2 new file mode 100644 index 00000000..2e35f4eb --- /dev/null +++ b/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:3ed0361d46fee45b792ab01bce498043fc666f814de55237f1fb5b6fc2d9876e +size 1982 diff --git a/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.aps2 b/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.aps2 new file mode 100644 index 00000000..46375518 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/Ramsey/Ramsey-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:c9814858177b26e6d187ee30920547db85d9ac00e2a16c6260cb84a9a2aa9a8f +size 1430 diff --git a/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.aps2 b/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.aps2 new file mode 100644 index 00000000..05f7d108 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0a347e86c8e15f8e8cc42c36f2c3fcd46cca15192d03ecf24de864a63f9ffed7 +size 40886 diff --git a/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.aps2 b/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.aps2 new file mode 100644 index 00000000..af7fa503 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SPAM/SPAM-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:693869c98824b15aaf89f43edfab1005b6382564d16b8545b3f1a9c5d07634b8 +size 23118 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.aps2 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.aps2 new file mode 100644 index 00000000..ebad8370 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:714f3bf47f611b39dd0fd544b7d517a5cb99ca52bd86a748d27ac6dc2cc17bea +size 101214 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.aps2 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.aps2 new file mode 100644 index 00000000..5b941d80 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5281ef48b48391cbc4542ad51a21a7dcfb3d34556b535ac47e8946c5a526980a +size 40174 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.aps2 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.aps2 new file mode 100644 index 00000000..f263f76a --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS3.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:153c21514cf092be2f49b28dd716a2ea3395b688dbe9d7a73122835f50aa8526 +size 94174 diff --git a/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.aps2 b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.aps2 new file mode 100644 index 00000000..5b941d80 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SimultaneousRB_AC/RB/RB-APS4.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5281ef48b48391cbc4542ad51a21a7dcfb3d34556b535ac47e8946c5a526980a +size 40174 diff --git a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.aps2 b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.aps2 new file mode 100644 index 00000000..26476cff --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:7d08252ecf794842f96ea5b7b2132283e351316258b1d42c963fc507e02034c5 +size 113798 diff --git a/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.aps2 b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.aps2 new file mode 100644 index 00000000..71ed0ab7 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SingleQubitRB/RB/RB-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fb7f55567c34c01fe5d88a02cdb2fdff6ad554eda1622d7aa5524c0d5abd038c +size 68670 diff --git a/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.aps2 b/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.aps2 new file mode 100644 index 00000000..1cfc0072 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e6697f675afe2b24ec3ba066632789487de2c4382d9592a2c22286d472c88fe5 +size 374 diff --git a/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.aps2 b/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.aps2 new file mode 100644 index 00000000..7cf89baf --- /dev/null +++ b/tests/test_data/awg/TestAPS2/SingleShot/SingleShot-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:fd31cbd1f5a73e2e315d16835183a44de585fe39b7eb150a4d4de70097a5cdbc +size 638 diff --git a/tests/test_data/awg/TestAPS2/Spec/Spec-APS1.aps2 b/tests/test_data/awg/TestAPS2/Spec/Spec-APS1.aps2 new file mode 100644 index 00000000..eded4da5 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/Spec/Spec-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:5c8b7bf6a97cb17eb48dd46c110a9087bb77a0934cf8053dcc9e6b9ad70bcde4 +size 294 diff --git a/tests/test_data/awg/TestAPS2/Spec/Spec-APS2.aps2 b/tests/test_data/awg/TestAPS2/Spec/Spec-APS2.aps2 new file mode 100644 index 00000000..0f81835b --- /dev/null +++ b/tests/test_data/awg/TestAPS2/Spec/Spec-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:921a848eed51b9289ca78c382f8bc1efb915f32a7cb2452ba41d0da2a2ab6dde +size 590 diff --git a/tests/test_data/awg/TestAPS2/T1/T1-APS1.aps2 b/tests/test_data/awg/TestAPS2/T1/T1-APS1.aps2 new file mode 100644 index 00000000..cd52185c --- /dev/null +++ b/tests/test_data/awg/TestAPS2/T1/T1-APS1.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:89a2e446a2e8efe6c2b532211dfb6245154566e3a4eaaffedc91172c89f6cdc9 +size 1590 diff --git a/tests/test_data/awg/TestAPS2/T1/T1-APS2.aps2 b/tests/test_data/awg/TestAPS2/T1/T1-APS2.aps2 new file mode 100644 index 00000000..591b8899 --- /dev/null +++ b/tests/test_data/awg/TestAPS2/T1/T1-APS2.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:eab22cfd02f7e5def78a2d83115256554074ea2202ec2406b99e46d4b07921af +size 1342 diff --git a/tests/test_data/multi-align.aps2 b/tests/test_data/multi-align.aps2 new file mode 100644 index 00000000..9d8fd6b2 --- /dev/null +++ b/tests/test_data/multi-align.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:0be50748c40f8b3bdafc4ff4daa193b607dae69808f0bad20c34b1b31ca32cce +size 5150 diff --git a/tests/test_data/multi-composite.aps2 b/tests/test_data/multi-composite.aps2 new file mode 100644 index 00000000..1499f19a --- /dev/null +++ b/tests/test_data/multi-composite.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:e992af922f5470372cd7471c401e39ec89c846f7c56652f3b9ecd622b5a3de20 +size 3614 diff --git a/tests/test_data/multi-operators.aps2 b/tests/test_data/multi-operators.aps2 new file mode 100644 index 00000000..98006e46 --- /dev/null +++ b/tests/test_data/multi-operators.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:ae9ec04d4585feae8d183dff7cab85c0ba4546b152a1a020421c6cbefc4daf38 +size 8294 diff --git a/tests/test_data/single-ramsey.aps2 b/tests/test_data/single-ramsey.aps2 new file mode 100644 index 00000000..317594ac --- /dev/null +++ b/tests/test_data/single-ramsey.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:20f946802a58a5eca8b380c3decbd1efa5b11af7e0218a9aff9b7d7667567361 +size 118358 diff --git a/tests/test_data/single-repeat.aps2 b/tests/test_data/single-repeat.aps2 new file mode 100644 index 00000000..5f927b97 --- /dev/null +++ b/tests/test_data/single-repeat.aps2 @@ -0,0 +1,3 @@ +version https://git-lfs.github.com/spec/v1 +oid sha256:79bb0e61d5cbc53767734ea544e111ad5670f1ab4cf9c2945ea7c4e3c82e8be9 +size 4054 From 8e5de51f8609d68b3dec010d7fbd573783634652 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 14 Feb 2019 13:18:49 -0500 Subject: [PATCH 106/235] APS1 binary format complete and passing unit tests --- QGL/PatternUtils.py | 2 +- QGL/drivers/APSPattern.py | 201 ++++++++-------- tests/compare_test_data.py | 45 ++-- tests/test_APS2Pattern.py | 1 - tests/test_APSPattern.py | 1 - tests/test_Compiler.py | 1 - tests/test_QGL.py | 2 - tests/test_Sequences.py | 226 +++++++++--------- .../awg/TestAPS1/AllXY/AllXY-APS1.aps1 | 4 +- .../awg/TestAPS1/AllXY/AllXY-APS1.h5 | 3 - .../awg/TestAPS1/CPMG/CPMG-APS1.aps1 | 4 +- .../test_data/awg/TestAPS1/CPMG/CPMG-APS1.h5 | 3 - .../awg/TestAPS1/Echo/Echo-APS1.aps1 | 4 +- .../test_data/awg/TestAPS1/Echo/Echo-APS1.h5 | 3 - .../EchoCRLen/EchoCR/EchoCR-APS1.aps1 | 4 +- .../TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 | 3 - .../EchoCRLen/EchoCR/EchoCR-APS2.aps1 | 4 +- .../TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 | 3 - .../EchoCRLen/EchoCR/EchoCR-APS3.aps1 | 4 +- .../TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 | 3 - .../EchoCRPhase/EchoCR/EchoCR-APS1.aps1 | 4 +- .../EchoCRPhase/EchoCR/EchoCR-APS1.h5 | 3 - .../EchoCRPhase/EchoCR/EchoCR-APS2.aps1 | 4 +- .../EchoCRPhase/EchoCR/EchoCR-APS2.h5 | 3 - .../EchoCRPhase/EchoCR/EchoCR-APS3.aps1 | 4 +- .../EchoCRPhase/EchoCR/EchoCR-APS3.h5 | 3 - .../awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 | 4 +- .../awg/TestAPS1/FlipFlop/FlipFlop-APS1.h5 | 3 - .../awg/TestAPS1/MISC1/MISC1-APS1.aps1 | 4 +- .../awg/TestAPS1/MISC1/MISC1-APS1.h5 | 3 - .../awg/TestAPS1/MISC2/MISC2-APS1.aps1 | 4 +- .../awg/TestAPS1/MISC2/MISC2-APS1.h5 | 3 - .../awg/TestAPS1/MISC2/MISC2-APS3.aps1 | 4 +- .../awg/TestAPS1/MISC2/MISC2-APS3.h5 | 3 - .../awg/TestAPS1/MISC3/MISC3-APS1.aps1 | 4 +- .../awg/TestAPS1/MISC3/MISC3-APS1.h5 | 3 - .../awg/TestAPS1/MISC3/MISC3-APS2.aps1 | 4 +- .../awg/TestAPS1/MISC3/MISC3-APS2.h5 | 3 - .../awg/TestAPS1/MISC3/MISC3-APS3.aps1 | 4 +- .../awg/TestAPS1/MISC3/MISC3-APS3.h5 | 3 - .../awg/TestAPS1/MISC4/MISC4-APS1.aps1 | 4 +- .../awg/TestAPS1/MISC4/MISC4-APS1.h5 | 3 - .../awg/TestAPS1/MISC4/MISC4-APS2.aps1 | 4 +- .../awg/TestAPS1/MISC4/MISC4-APS2.h5 | 3 - .../awg/TestAPS1/MISC4/MISC4-APS3.aps1 | 4 +- .../awg/TestAPS1/MISC4/MISC4-APS3.h5 | 3 - .../awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 | 4 +- .../awg/TestAPS1/PiRabi/PiRabi-APS1.h5 | 3 - .../awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 | 4 +- .../awg/TestAPS1/PiRabi/PiRabi-APS2.h5 | 3 - .../awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 | 4 +- .../awg/TestAPS1/PiRabi/PiRabi-APS3.h5 | 3 - .../awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 | 4 +- .../awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.h5 | 3 - .../awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 | 4 +- .../awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.h5 | 3 - .../awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 | 4 +- .../awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.h5 | 3 - .../TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 | 4 +- .../awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.h5 | 3 - .../TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 | 4 +- .../awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.h5 | 3 - .../awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.h5 | 3 - .../awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 | 4 +- .../awg/TestAPS1/Ramsey/Ramsey-APS1.h5 | 3 - .../awg/TestAPS1/SPAM/SPAM-APS1.aps1 | 4 +- .../test_data/awg/TestAPS1/SPAM/SPAM-APS1.h5 | 3 - .../TestAPS1/SimultaneousRB_AC/RB/RB-APS1.h5 | 3 - .../TestAPS1/SimultaneousRB_AC/RB/RB-APS2.h5 | 3 - .../SimultaneousRB_AC/RB/RB-APS2_py27.h5 | 3 - .../TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 | 4 +- .../awg/TestAPS1/SingleQubitRB/RB/RB-APS1.h5 | 3 - .../TestAPS1/SingleQubitRB/RB/RB-APS1_py27.h5 | 3 - .../TestAPS1/SingleShot/SingleShot-APS1.aps1 | 4 +- .../TestAPS1/SingleShot/SingleShot-APS1.h5 | 3 - .../awg/TestAPS1/Spec/Spec-APS1.aps1 | 4 +- .../test_data/awg/TestAPS1/Spec/Spec-APS1.h5 | 3 - tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 | 4 +- tests/test_data/awg/TestAPS1/T1/T1-APS1.h5 | 3 - utils/convert_h5_to_aps.py | 57 +++-- 80 files changed, 324 insertions(+), 458 deletions(-) delete mode 100644 tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/Echo/Echo-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.h5 delete mode 100644 tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1_py27.h5 delete mode 100644 tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/Spec/Spec-APS1.h5 delete mode 100644 tests/test_data/awg/TestAPS1/T1/T1-APS1.h5 diff --git a/QGL/PatternUtils.py b/QGL/PatternUtils.py index a718082a..9298d45e 100644 --- a/QGL/PatternUtils.py +++ b/QGL/PatternUtils.py @@ -334,5 +334,5 @@ def flatten_pulses(): awg, [str(p) for p in ps.values()])) continue print("Updating pulses for {}".format(awg)) - translators[awg].update_wf_library(path + "-" + awg + ".h5", ps, + translators[awg].update_wf_library(path + "-" + awg + ".aps", ps, offsets) diff --git a/QGL/drivers/APSPattern.py b/QGL/drivers/APSPattern.py index 226a67a4..c6a1260f 100644 --- a/QGL/drivers/APSPattern.py +++ b/QGL/drivers/APSPattern.py @@ -17,10 +17,10 @@ ''' import os +import struct import numpy as np from warnings import warn -from itertools import chain -from future.moves.itertools import zip_longest +from itertools import chain, zip_longest from QGL import Compiler, ControlFlow, BlockLabel, PatternUtils from QGL.PatternUtils import hash_pulse, flatten from copy import copy, deepcopy @@ -658,7 +658,7 @@ def unroll_loops(LLs): def write_sequence_file(awgData, fileName, miniLLRepeat=1): ''' - Main function to pack channel LLs into an APS h5 file. + Main function to pack channel LLs into an APS1 file. ''' #Preprocess the sequence data to handle APS restrictions LLs12, repeat12, wfLib12 = preprocess(awgData['ch12']['linkList'], @@ -676,11 +676,10 @@ def write_sequence_file(awgData, fileName, miniLLRepeat=1): merge_APS_markerData(LLs12, awgData['ch2m1']['linkList'], 2) merge_APS_markerData(LLs34, awgData['ch3m1']['linkList'], 1) merge_APS_markerData(LLs34, awgData['ch4m1']['linkList'], 2) - #Open the HDF5 file + if os.path.isfile(fileName): os.remove(fileName) - print("WRiting ", fileName) with open(fileName, 'wb') as FID: channelDataFor = np.array([0,0,0,0], dtype=np.bool) if LLs12: @@ -702,40 +701,25 @@ def write_sequence_file(awgData, fileName, miniLLRepeat=1): for key, wf in wfLib.items()})) wfInfo.append(create_wf_vector({key: wf.imag for key, wf in wfLib.items()})) - #Create the groups and datasets for chanct in range(4): - # chanStr = '/chan_{0}'.format(chanct + 1) - # chanGroup = FID.create_group(chanStr) FID.write(np.uint8(1).tobytes()) # isIQMode FID.write(np.uint64(wfInfo[chanct][0].size).tobytes()) # Length of waveforms FID.write(wfInfo[chanct][0].tobytes()) # Waveforms np.int16 - # chanGroup.attrs['isIQMode'] = np.uint8(1) - #Write the waveformLib to file - # FID.create_dataset('{0}/waveformLib'.format(chanStr), - # data=wfInfo[chanct][0]) - for chanct in [1,3]: + FID.write(np.uint8(len(LLData[0]) > 0).tobytes()) # LL for chan1 + FID.write(np.uint8(len(LLData[1]) > 0).tobytes()) # LL for chan3 + for chanct in [0,2]: #For A channels (1 & 3) we write link list data if we actually have any if LLData[chanct // 2]: - # groupStr = chanStr + '/linkListData' - # LLGroup = FID.create_group(groupStr) LLDataVecs, numEntries = create_LL_data( LLData[chanct // 2], wfInfo[chanct][1], os.path.basename(fileName)) - # LLGroup.attrs['length'] = numEntries FID.write(np.uint64(len(LLDataVecs.keys())).tobytes()) # numKeys FID.write(np.uint64(numEntries).tobytes()) # numEntries - - # print(numEntries, len(LLDataVecs.keys())) for key, dataVec in LLDataVecs.items(): - print(key, dataVec.dtype) FID.write(key.ljust(32,"#").encode("utf-8")) # Key 32 byte utf-8 FID.write(dataVec.tobytes()) # Data np.uint16 - # FID.create_dataset(groupStr + '/' + key, data=dataVec) - - # else: - # chanGroup.attrs['isLinkListData'] = np.uint8(0) def read_sequence_file(fileName): @@ -752,90 +736,101 @@ def read_sequence_file(fileName): chanStrs = ['ch1', 'ch2', 'ch3', 'ch4'] chanStrs2 = ['chan_1', 'chan_2', 'chan_3', 'chan_4'] mrkStrs = ['ch1m1', 'ch2m1', 'ch3m1', 'ch4m1'] - + + data = {} with open(fileName, 'rb') as FID: target_hw = FID.read(4).decode('utf-8') file_version = struct.unpack(' 0: - trigger_delays.append(cum_time + trigger1[ct]) - else: - if trigger2[ct] > 0: - trigger_delays.append(cum_time + trigger2[ct]) - - #waveforms - wf_repeat = (repeat[ct] & REPEAT_MASK) + 1 - if TA_PAIR_MASK & repeat[ct]: - AWGData[chanStrs[chanct]][-1].append( - (wf_repeat * count[ct], wf_lib[addr[ct]])) - else: - for repct in range(wf_repeat): - for sample in wf_lib[addr[ct]:addr[ct] + count[ - ct]]: - AWGData[chanStrs[chanct]][-1].append( - (1, sample)) - - cum_time += count[ct] - - #Create the trigger sequence - if END_MINILL_MASK & repeat[ct]: - AWGData[mrkStrs[chanct]].append([]) - for delay in np.diff(trigger_delays): - AWGData[mrkStrs[chanct]][-1].append((delay - 1, 0)) - AWGData[mrkStrs[chanct]][-1].append((1, 1)) - - return curLLdata,AWGData + miniLLRepeat = struct.unpack('?', FID.read(1))[0] + + # channels = [chanStrs2[i] for i in range(4) if channelDataFor[i]] + channels = chanStrs2 + for channel in channels: + data[channel] = {} + data[channel]["isIQMode"] = bool(struct.unpack('?', FID.read(1))[0]) + wf_len = struct.unpack(' 0: + trigger_delays.append(cum_time + trigger1[ct]) + else: + if trigger2[ct] > 0: + trigger_delays.append(cum_time + trigger2[ct]) + + #waveforms + wf_repeat = (repeat[ct] & REPEAT_MASK) + 1 + if TA_PAIR_MASK & repeat[ct]: + AWGData[chanStrs[chanct]][-1].append( + (wf_repeat * count[ct], wf_lib[addr[ct]])) + else: + for repct in range(wf_repeat): + for sample in wf_lib[addr[ct]:addr[ct] + count[ + ct]]: + AWGData[chanStrs[chanct]][-1].append( + (1, sample)) + + cum_time += count[ct] + + #Create the trigger sequence + if END_MINILL_MASK & repeat[ct]: + AWGData[mrkStrs[chanct]].append([]) + for delay in np.diff(trigger_delays): + AWGData[mrkStrs[chanct]][-1].append((delay - 1, 0)) + AWGData[mrkStrs[chanct]][-1].append((1, 1)) + + return AWGData if __name__ == '__main__': diff --git a/tests/compare_test_data.py b/tests/compare_test_data.py index 34f7daa8..3a49a58c 100644 --- a/tests/compare_test_data.py +++ b/tests/compare_test_data.py @@ -1,7 +1,6 @@ import os import sys import glob -import h5py from builtins import input from QGL import * @@ -18,17 +17,14 @@ def compare_sequences(seq_filter=""): for test in testdirs: # build up subdirectory name _, name = os.path.split(test) - testfiles = glob.glob(os.path.join(test, '*.h5')) + testfiles = glob.glob(os.path.join(test, '*.aps*')) # recurse into subdirectories while len(testfiles) == 1 and os.path.isdir(testfiles[0]): _, subname = os.path.split(testfiles[0]) name = os.path.join(name, subname) testfiles = glob.glob(os.path.join(testfiles[0], '*')) newpath = os.path.join(BASE_AWG_DIR, subdir, name) - newfiles = glob.glob(os.path.join(newpath, '*.h5')) - #filter py27 look for py27 versions - testfiles = filter_py27(testfiles) - newfiles = filter_py27(newfiles) + newfiles = glob.glob(os.path.join(newpath, '*.aps*')) if seq_filter and any(seq_filter in seq_file for seq_file in testfiles): print("{0} comparing to {1}".format(test, newpath)) PulseSequencePlotter.plot_pulse_files_compare(testfiles, newfiles) @@ -36,32 +32,19 @@ def compare_sequences(seq_filter=""): if c == 'q': break - -def filter_py27(filenames): - py27_files = [f for f in filenames if f[-8:] == "_py27.h5"] - if py27_files: - if sys.version_info[0] > 2: - #strip them out for python3 (and above?) - filenames = [f for f in filenames if f[-8:] != "_py27.h5"] - else: - #otherwise strip the non "_py27" version - filenames = [f for f in filenames - if f.replace(".h5", "_py27.h5") not in py27_files] - return filenames - - def update_test_files(): - for device in ['APS1', 'APS2']: - testdirs = glob.glob(os.path.join(BASE_TEST_DIR, 'Test' + device, '*')) - for test in testdirs: - testfiles = glob.glob(os.path.join(test, '*')) - # recurse into subdirectories - while len(testfiles) == 1 and os.path.isdir(testfiles[0]): - testfiles = glob.glob(os.path.join(testfiles[0], '*')) - for tfile in testfiles: - FID = h5py.File(tfile) - FID['/'].attrs['target hardware'] = device - FID.close() + raise Exception("update_test_files must be updated for new binary format") + # for device in ['APS1', 'APS2']: + # testdirs = glob.glob(os.path.join(BASE_TEST_DIR, 'Test' + device, '*')) + # for test in testdirs: + # testfiles = glob.glob(os.path.join(test, '*')) + # # recurse into subdirectories + # while len(testfiles) == 1 and os.path.isdir(testfiles[0]): + # testfiles = glob.glob(os.path.join(testfiles[0], '*')) + # for tfile in testfiles: + # FID = h5py.File(tfile) + # FID['/'].attrs['target hardware'] = device + # FID.close() if __name__ == '__main__': diff --git a/tests/test_APS2Pattern.py b/tests/test_APS2Pattern.py index 54dbd1ac..08874cea 100644 --- a/tests/test_APS2Pattern.py +++ b/tests/test_APS2Pattern.py @@ -1,4 +1,3 @@ -import h5py import unittest import numpy as np from copy import copy diff --git a/tests/test_APSPattern.py b/tests/test_APSPattern.py index 3bfa2534..f2e8d3e5 100644 --- a/tests/test_APSPattern.py +++ b/tests/test_APSPattern.py @@ -1,4 +1,3 @@ -import h5py import unittest import numpy as np diff --git a/tests/test_Compiler.py b/tests/test_Compiler.py index 781bb49c..7754b72a 100644 --- a/tests/test_Compiler.py +++ b/tests/test_Compiler.py @@ -1,4 +1,3 @@ -import h5py import unittest import numpy as np diff --git a/tests/test_QGL.py b/tests/test_QGL.py index c5db5b52..6255d509 100644 --- a/tests/test_QGL.py +++ b/tests/test_QGL.py @@ -1,4 +1,3 @@ -import h5py import unittest import numpy as np import time @@ -6,7 +5,6 @@ import struct from bbndb.qgl import PhysicalChannel, LogicalChannel, Measurement from QGL import * -import h5py # Waveform numpy assert_allclose Test for QGL # diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index 2c0cc836..9cb343b8 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -6,7 +6,7 @@ import QGL from QGL.Channels import Edge, Measurement, LogicalChannel, LogicalMarkerChannel, PhysicalMarkerChannel, PhysicalQuadratureChannel -from QGL.drivers import APSPattern, APS2Pattern, TekPattern +from QGL.drivers import APSPattern, APS2Pattern # Pulled in logger to help debug stand-alone run issue with the config.AWGDir # (the configuration was NOT gettng loaded when run independently) @@ -111,8 +111,10 @@ def set_awg_dir(self, footer=""): logger.warning( "#----- Post load-config QGL.config.AWGDir: {%s}.\n\r", QGL.config.AWGDir) #else: # logger.warning( "\n\r#----- Using QGL.config.AWGDir {%s} 8-p", QGL.config.AWGDir) + if not hasattr(self, 'original_awg_dir'): + self.original_awg_dir = QGL.config.AWGDir - self.awg_dir = os.path.abspath(QGL.config.AWGDir + os.path.sep + cn) + self.awg_dir = os.path.abspath(self.original_awg_dir + os.path.sep + cn) self.truth_dir = os.path.abspath(self.testFileDirectory + os.path.sep + cn) @@ -503,116 +505,116 @@ def test_mux_CR(self): self.compare_sequences('CNOT_CR_mux') -# class TestAPS1(unittest.TestCase, AWGTestHelper, TestSequences): -# def setUp(self): -# AWGTestHelper.__init__(self, APSPattern) -# for name in ['APS1', 'APS2', 'APS3']: -# for ch in ['12', '34']: -# channelName = name + '-' + ch -# channel = PhysicalQuadratureChannel(label=channelName) -# channel.sampling_rate = 1.2e9 -# channel.instrument = name -# channel.translator = 'APSPattern' -# self.channels[channelName] = channel - -# for m in range(1, 5): -# channelName = "{0}-{1}m1".format(name, m) -# channel = PhysicalMarkerChannel(label=channelName) -# channel.sampling_rate = 1.2e9 -# channel.instrument = name -# channel.translator = 'APSPattern' -# self.channels[channelName] = channel - -# mapping = {'digitizerTrig': 'APS1-1m1', -# 'slave_trig': 'APS1-2m1', -# 'q1': 'APS1-12', -# 'M-q1': 'APS1-34', -# 'M-q1-gate': 'APS1-3m1', -# 'q1-gate': 'APS1-4m1', -# 'q2': 'APS2-12', -# 'M-q2': 'APS2-34', -# 'M-q2-gate': 'APS2-1m1', -# 'q2-gate': 'APS2-2m1', -# 'cr': 'APS3-12', -# 'cr-gate': 'APS3-1m1', -# 'M-q1q2': 'APS3-34', -# 'M-q1q2-gate': 'APS3-2m1'} - -# # override trigger lengths on APS1 to get single blips -# self.channels['slave_trig'].pulse_params['length'] = 0.833e-9 -# self.channels['digitizerTrig'].pulse_params['length'] = 0.833e-9 -# self.finalize_map(mapping) - -# def compare_file_data(self, testFile, truthFile): -# ''' -# Override the method in AWGTestHelper so that we can special-case marker comparison -# ''' -# awgData = self.read_function(testFile) -# truthData = self.read_function(truthFile) - -# awgDataLen = len(awgData) -# truthDataLen = len(truthData) - -# self.assertTrue(awgDataLen == truthDataLen, -# "Expected {0} sequences in file. Found {1}.".format( -# truthDataLen, awgDataLen)) - -# for name in truthData: -# self.assertTrue( -# name in awgData, -# "Expected channel {0} not found in file {1}".format(name, -# testFile)) -# isMarker = ('m' == name[-2]) - -# for x in range(len(truthData[name])): -# seqA = np.array(truthData[name][x]) -# seqB = np.array(awgData[name][x]) -# if isMarker: -# self.compare_marker_sequence( -# seqA, seqB, -# "\nFile {0} =>\nChannel {1} Sequence {2}".format( -# testFile, name, x)) -# else: -# self.compare_sequence( -# seqA, seqB, -# "\nFile {0} =>\nChannel {1} Sequence {2}".format( -# testFile, name, x)) - -# def compare_marker_sequence(self, seqA, seqB, errorHeader): -# markerDistanceTolerance = 4 -# self.assertTrue(seqA.size == seqB.size, -# "{0} size {1} != size {2}".format( -# errorHeader, str(seqA.size), str(seqB.size))) - -# # convert sequences to locations of blips -# idxA = np.where(seqA)[0] -# idxB = np.where(seqB)[0] -# self.assertTrue( -# len(idxA) == len(idxB), -# "{0}.\nNumber of blips did not match: {1} != {2}".format( -# errorHeader, len(idxA), len(idxB))) -# # compare the blip locations element-wise -# if len(idxA) > 0: -# diff = np.abs(idxA - idxB) -# else: -# diff = np.array([0]) -# self.assertTrue( -# max(diff) <= markerDistanceTolerance, -# "{0}\nMismatches: {1}".format(errorHeader, diff.nonzero()[0])) - -# @unittest.expectedFailure -# def test_Rabi_RabiWidth(self): -# """ test_Rabi_RabiWidth is expected to fail on APS1 with the following error: -# AssertionError: Oops! You have exceeded the waveform memory of the APS -# """ -# TestSequences.test_Rabi_RabiWidth(self) - -# @unittest.expectedFailure -# def test_RB_SimultaneousRB_AC(self): -# """ test_RB_SimultaneousRB_AC is expected to fail on APS1 with the following error: -# AssertionError: Oops! You have exceeded the waveform memory of the APS -# """ -# TestSequences.test_RB_SimultaneousRB_AC(self) +class TestAPS1(unittest.TestCase, AWGTestHelper, TestSequences): + def setUp(self): + AWGTestHelper.__init__(self, APSPattern) + for name in ['APS1', 'APS2', 'APS3']: + for ch in ['12', '34']: + channelName = name + '-' + ch + channel = PhysicalQuadratureChannel(label=channelName) + channel.sampling_rate = 1.2e9 + channel.instrument = name + channel.translator = 'APSPattern' + self.channels[channelName] = channel + + for m in range(1, 5): + channelName = "{0}-{1}m1".format(name, m) + channel = PhysicalMarkerChannel(label=channelName) + channel.sampling_rate = 1.2e9 + channel.instrument = name + channel.translator = 'APSPattern' + self.channels[channelName] = channel + + mapping = {'digitizerTrig': 'APS1-1m1', + 'slave_trig': 'APS1-2m1', + 'q1': 'APS1-12', + 'M-q1': 'APS1-34', + 'M-q1-gate': 'APS1-3m1', + 'q1-gate': 'APS1-4m1', + 'q2': 'APS2-12', + 'M-q2': 'APS2-34', + 'M-q2-gate': 'APS2-1m1', + 'q2-gate': 'APS2-2m1', + 'cr': 'APS3-12', + 'cr-gate': 'APS3-1m1', + 'M-q1q2': 'APS3-34', + 'M-q1q2-gate': 'APS3-2m1'} + + # override trigger lengths on APS1 to get single blips + self.channels['slave_trig'].pulse_params['length'] = 0.833e-9 + self.channels['digitizerTrig'].pulse_params['length'] = 0.833e-9 + self.finalize_map(mapping) + + def compare_file_data(self, testFile, truthFile): + ''' + Override the method in AWGTestHelper so that we can special-case marker comparison + ''' + awgData = self.read_function(testFile) + truthData = self.read_function(truthFile) + + awgDataLen = len(awgData) + truthDataLen = len(truthData) + + self.assertTrue(awgDataLen == truthDataLen, + "Expected {0} sequences in file. Found {1}.".format( + truthDataLen, awgDataLen)) + + for name in truthData: + self.assertTrue( + name in awgData, + "Expected channel {0} not found in file {1}".format(name, + testFile)) + isMarker = ('m' == name[-2]) + + for x in range(len(truthData[name])): + seqA = np.array(truthData[name][x]) + seqB = np.array(awgData[name][x]) + if isMarker: + self.compare_marker_sequence( + seqA, seqB, + "\nFile {0} =>\nChannel {1} Sequence {2}".format( + testFile, name, x)) + else: + self.compare_sequence( + seqA, seqB, + "\nFile {0} =>\nChannel {1} Sequence {2}".format( + testFile, name, x)) + + def compare_marker_sequence(self, seqA, seqB, errorHeader): + markerDistanceTolerance = 4 + self.assertTrue(seqA.size == seqB.size, + "{0} size {1} != size {2}".format( + errorHeader, str(seqA.size), str(seqB.size))) + + # convert sequences to locations of blips + idxA = np.where(seqA)[0] + idxB = np.where(seqB)[0] + self.assertTrue( + len(idxA) == len(idxB), + "{0}.\nNumber of blips did not match: {1} != {2}".format( + errorHeader, len(idxA), len(idxB))) + # compare the blip locations element-wise + if len(idxA) > 0: + diff = np.abs(idxA - idxB) + else: + diff = np.array([0]) + self.assertTrue( + max(diff) <= markerDistanceTolerance, + "{0}\nMismatches: {1}".format(errorHeader, diff.nonzero()[0])) + + @unittest.expectedFailure + def test_Rabi_RabiWidth(self): + """ test_Rabi_RabiWidth is expected to fail on APS1 with the following error: + AssertionError: Oops! You have exceeded the waveform memory of the APS + """ + TestSequences.test_Rabi_RabiWidth(self) + + @unittest.expectedFailure + def test_RB_SimultaneousRB_AC(self): + """ test_RB_SimultaneousRB_AC is expected to fail on APS1 with the following error: + AssertionError: Oops! You have exceeded the waveform memory of the APS + """ + TestSequences.test_RB_SimultaneousRB_AC(self) if __name__ == "__main__": unittest.main() diff --git a/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 b/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 index 7fba53b0..9578434c 100644 --- a/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:c79184b0806766b42dc92f873beea634e2292d60edda5be14065eaa519206f5a -size 3989 +oid sha256:93a7ed05a9c679de05915e51500f6d9ee9d4e5ef0016b1a8c2ff26bdf622b1e0 +size 3991 diff --git a/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.h5 b/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.h5 deleted file mode 100644 index d4b9ab22..00000000 --- a/tests/test_data/awg/TestAPS1/AllXY/AllXY-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:598ec4cd4a90c6ef37b3bf78a6a07e093dafd68b23037179dc8f3ee604196ff3 -size 20568 diff --git a/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 b/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 index 9d9b903c..7f055d16 100644 --- a/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:492602ef1ac6e451f6b7f531470af0f23b31d69fd2d2d2e0e1ea1bbd770eaf6d -size 2355 +oid sha256:f224b52bf73aeb376166df84f6f166e5d48a7c4a2214a73fd40e41c3ef52e96b +size 2357 diff --git a/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.h5 b/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.h5 deleted file mode 100644 index 8e80f58b..00000000 --- a/tests/test_data/awg/TestAPS1/CPMG/CPMG-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dd0994b6ef60cc0a19315aa45512f1c7f6a6612ce06895f161f4ae60fa7917d7 -size 18520 diff --git a/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 b/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 index bf09543a..42b91ac5 100644 --- a/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:85cc8c04c27d1dacdec56d3942a65ac268da07a523226bfd57607e18e587d532 -size 2925 +oid sha256:b35762b99a622467e594bf295ed35d923ff944bc6aa6c07a7832895a0c53f8f6 +size 2927 diff --git a/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.h5 b/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.h5 deleted file mode 100644 index 00c28e4d..00000000 --- a/tests/test_data/awg/TestAPS1/Echo/Echo-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0f14162bcf50abb771326b2db3ea36288e704d1224dbd1660dbec60de215121f -size 20568 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 index 2ae5e8b8..9bb93800 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8fb5139183372cc3e055c69590c7556c933db502ed2333c4d7856efdc2f67462 -size 4207 +oid sha256:9fe524d7623feccacec0293a877c02afc44f75ee77d1d5269f795a85de341b7c +size 4209 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 deleted file mode 100644 index 0f589d2c..00000000 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:f1e0f1039e90c8361735fe98ddbd24e434fb9b760169a62ec946ae40274d1a60 -size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 index 057f5af0..969d00e7 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:48193962f6abc6b1d8c0b4242c8c260e948fc9e7ed706d4971702277cc943698 -size 3147 +oid sha256:2e3dc99287d4dc53f54900f61f8c3390214e6dbaeb065f991a9dd513a83bd63d +size 3149 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 deleted file mode 100644 index b5872b5f..00000000 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:92b69fd2c1d4208e22cb652dfac0e78de7d7502fc1a0028e4df32c7319cd9d02 -size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 index 590ec1bc..67db4db5 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 +++ b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2db9971c60a9df840a9443e5a1d381db92d9da9d818ad213b527efe0f179ef89 -size 3367 +oid sha256:2e7b0034fd155a7fd2d37388d977b0af521b194b1c47b3e8f78d5fa86b22861d +size 3369 diff --git a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 deleted file mode 100644 index 953bfa04..00000000 --- a/tests/test_data/awg/TestAPS1/EchoCRLen/EchoCR/EchoCR-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:aa055382df427f2625eb61ec6c112603c36891bf69e2e2d5037e2ef0af8d725b -size 18136 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 index eaed969e..b30daca4 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:979b141165b4d0a5292329a0cbaeb18dea7de66c1670bb8da99b22c42ba9016f -size 4207 +oid sha256:71c17a0aa26078892e47aeb2ae0dcea71532b6b614a42ad90a15f7a07c1e25ab +size 4209 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 deleted file mode 100644 index 9c6f0102..00000000 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:e1bc671bcfbba1005d9e593edf45288d0b6d830ff0445cf6c9360c31daa0753b -size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 index 80486dc5..0b249f5b 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:2516fbc0a51c9fbf54c1c3126911e76bee7c1cf3003ee743c82c8ff35cf99edc -size 3511 +oid sha256:3059c3d4b37c9f9de1e95e31f3a14bcdfd8f1017189ef697bbaeab4419dd8caa +size 3513 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 deleted file mode 100644 index 041cb89c..00000000 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8e1b9e57467e487b23c15743b839e640c168bb5b0fe48fc55df83ade33e69558 -size 20552 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 index fd1dcc19..7c0fccb8 100644 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 +++ b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cc3fc6bde52e7723effe78eb35d02a21e2721dc66d6e825435d6ec9937cb7bf7 -size 11407 +oid sha256:b99cd5f2d629ff33ee53b070b0a687d000fa9681bcaac3bbd3b3a71ec87e0ccb +size 11409 diff --git a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 b/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 deleted file mode 100644 index 9898ef3b..00000000 --- a/tests/test_data/awg/TestAPS1/EchoCRPhase/EchoCR/EchoCR-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c5e77f05e76a9b655cccc9c76aae4f3e412c88f4e7259b18db9a2a1b21c0fe4e -size 26992 diff --git a/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 b/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 index a243b583..e185c0d9 100644 --- a/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:1d1157dea39f1b1c023983268a96f87b90fc364875120605f6a1c2bd2b66f12e -size 23529 +oid sha256:8dcc6f62fb04a90ae74b6b80f2963736145fcd3672762641ac1dcc74c6f0c985 +size 23531 diff --git a/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.h5 b/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.h5 deleted file mode 100644 index 5c65333c..00000000 --- a/tests/test_data/awg/TestAPS1/FlipFlop/FlipFlop-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:757f73118a7c633b5d5161401bab8443bb4bb98a965b1f9978c71f9d0d694149 -size 41192 diff --git a/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 index ff116d24..eb78325e 100644 --- a/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:6aec33015183570c3f23149df34361064ec81158c5be7b6dfa42c427c9f2b003 -size 3841 +oid sha256:0082cb2b408b942eac05a48e77344cb3f63edd606f6a7849041652b8f2852559 +size 3843 diff --git a/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.h5 b/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.h5 deleted file mode 100644 index 74151403..00000000 --- a/tests/test_data/awg/TestAPS1/MISC1/MISC1-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:964f74ba4a169ab0f60f9f39aeaa4f497f7a63f339d4773ee5e4b563b8859056 -size 20568 diff --git a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 index 15b31644..c79110f8 100644 --- a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9141cca1496506a615c568dbdf36a741c6ed953ff49773c1a0b0444566d31541 -size 657 +oid sha256:1b4d6e3c6244e4efad5925136379931db5be619a147bd82f9e6e2b7ae92e1397 +size 659 diff --git a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.h5 b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.h5 deleted file mode 100644 index adc1bfc8..00000000 --- a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:8f7e131852cac2f0b8dc2a9dbcd738be9dc1b5e8bc0323d06362c5d5a49cc372 -size 18520 diff --git a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 index 048d8dde..386f8af3 100644 --- a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:54b3fd28c21e1dea5b7367782a2c5cecdd66407e5566b01c5c3dd3c958d98d1e -size 753 +oid sha256:11a4702cfb925a868d316c4549786d225e0ac115a8395640522c9221ab4e5b74 +size 755 diff --git a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.h5 b/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.h5 deleted file mode 100644 index 43c67a89..00000000 --- a/tests/test_data/awg/TestAPS1/MISC2/MISC2-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:79a4a4a1cf2cab3bedbf060b4dc4bf7166eaabe7781de4dcc0bf9d1923075b5f -size 16104 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 index b87471d5..673bf3d7 100644 --- a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8686964ef675b7ddfe0d038a823237e8d8a02a79169fbf1d5f946f7a2347a8ee -size 683 +oid sha256:efe2ba032c43485ea55dd8b9cc9470ce19968eca2dabf744a958f7d6a369cefe +size 685 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.h5 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.h5 deleted file mode 100644 index e9be50b1..00000000 --- a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6d059bb8d6ea48d80336179c280d8fbff658165f7823b85f173dd541205068ca -size 18520 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 index 4f15b5ef..197a426e 100644 --- a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:e33b8bfca6d77c3f4db6de021f3aaddf38d2f0ef5b31b0c005c173a2c275385a -size 441 +oid sha256:f711c3304df1cfc8c32f5ae85de38023c33f070bd89b86752ad09992e35c37e9 +size 443 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.h5 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.h5 deleted file mode 100644 index ee2a676f..00000000 --- a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:85c3c4ede4a0cc4f75fe45a99c712982af0a90cd12e3131bbc447f460e379384 -size 16104 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 index a5ca693b..d076e9bf 100644 --- a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:f8d6633b20b4405ae56a260a36874486a816b734e8f479eef8aab7d4522c34e5 -size 753 +oid sha256:bb173426f6db57e38d836a81eebb96058918ef7403406184ece584fd447599ac +size 755 diff --git a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.h5 b/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.h5 deleted file mode 100644 index f5180ba9..00000000 --- a/tests/test_data/awg/TestAPS1/MISC3/MISC3-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a79d97a95d1f4d010df4f9a9116d92099d9920c3fec64cf456d1ba71fcb4c506 -size 16104 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 index e4a0ba9a..f4984818 100644 --- a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:7d1182d7ee93b7e7990b518b42c85b8483247203922c831c3fb386ced529d0a5 -size 1149 +oid sha256:f9f9c622f7a08a002c42b382b3c2b12ecc97f3625c79e9cb64dae118af52570f +size 1151 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.h5 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.h5 deleted file mode 100644 index 8c50f48f..00000000 --- a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:c0e436ad63f7ce73f53211ce6ceeff3e2d4d880fbeb6c942e16142f3294deab2 -size 18520 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 index 7c0d364c..28147007 100644 --- a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:cf1666a6e70851c52a0f27240300cf620462539c63df9b5ec8d9f022c4d9ce42 -size 903 +oid sha256:3faa203b1c058d66a6499f9aba6e1cc04d49129e973bc131f900b26fc5c1ebfd +size 905 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.h5 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.h5 deleted file mode 100644 index 85cf1477..00000000 --- a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a0915cda20a3a66881b7701da17189c2be8dda857dcbe7180ac27ec8509c3b51 -size 16104 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 index a7c51ea4..6161fa40 100644 --- a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 +++ b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:b6e7be64402e6eee1152e294b53228eccdb37840fb243e68e452776951dcb8de -size 773 +oid sha256:4ef90629a3658816f53523c9e2e80d96705187bd68e835f0d468dda368e9a694 +size 775 diff --git a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.h5 b/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.h5 deleted file mode 100644 index 7c6ded4c..00000000 --- a/tests/test_data/awg/TestAPS1/MISC4/MISC4-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:65be23387c62f3a8a08547cea77a6e9ff8c649301fe60b33bacfbd96fc9433a5 -size 16104 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 index f34a039b..10fc0bb5 100644 --- a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:4c14c23f7b2e24985a2a800b16e3786c8985470aa440a97c972ff8f88554d435 -size 2997 +oid sha256:f2979185871f56a5143138c031de487b54aacc4a4085689f6b630a48d5afd440 +size 2999 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.h5 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.h5 deleted file mode 100644 index bb89df1a..00000000 --- a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:665f63cb2a9ba48fdd3e890c3db66f0607d4e20263d8d77db35c77bfda76c63c -size 20568 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 index c61a61e5..2b990dea 100644 --- a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ff2b0e15651317fdb1dcb320a73d56954f00770eb866ce6843cb3d18b018d65c -size 2377 +oid sha256:ca5bdf91c5f9d3d1d4c4ac67bad05c6418a4db32c53949a82adfa82a86289800 +size 2379 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.h5 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.h5 deleted file mode 100644 index 99029ac8..00000000 --- a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:99adec81a02115317caeddc332063fef5fd5bcb5342a3a15ad92fb5567a6c3fc -size 18520 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 index 76341295..a6e4f937 100644 --- a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 +++ b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:9830bdd5c69c8ddb7e0656e78c4a06b61b205a5de7e7fb620ae7c1f423d79667 -size 1887 +oid sha256:269cf8968cc6d1c11426b6db80ba679e50269528eebff2647a4d64376f8e1cf9 +size 1889 diff --git a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.h5 b/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.h5 deleted file mode 100644 index dce42a8f..00000000 --- a/tests/test_data/awg/TestAPS1/PiRabi/PiRabi-APS3.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6dc366b846ff17aea66ecfe7154088da73ae3803033c3ba2a90cba8ab4b3ac13 -size 16104 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 index 0066ecad..4bc0eb82 100644 --- a/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c844118df061ae11cbf8f5e66ccad2b51b3d838181b0e3ccb2d006cd386b98e -size 2903 +oid sha256:03384c3fcbe4ebeb39a43df1e47173d5276ff431c44cd10c6a5891414c12022d +size 2905 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.h5 deleted file mode 100644 index e21de48c..00000000 --- a/tests/test_data/awg/TestAPS1/RabiAmp/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1878b25a724b98b176070fbb562b7a54ba4f6d0ca67724e4b5bc31a43e491fae -size 20568 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 index 0066ecad..4bc0eb82 100644 --- a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:0c844118df061ae11cbf8f5e66ccad2b51b3d838181b0e3ccb2d006cd386b98e -size 2903 +oid sha256:03384c3fcbe4ebeb39a43df1e47173d5276ff431c44cd10c6a5891414c12022d +size 2905 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.h5 deleted file mode 100644 index e21de48c..00000000 --- a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:1878b25a724b98b176070fbb562b7a54ba4f6d0ca67724e4b5bc31a43e491fae -size 20568 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 index 459987f9..ce5c9406 100644 --- a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:55ccffcdd6ff26268d185054506ebc65fb9e67eded775011073d9f77922deb94 -size 2803 +oid sha256:4e5c52c3c9a278381d328e3388d75d4d2296041c0c1a6c1c0a3083a8459b087f +size 2805 diff --git a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.h5 b/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.h5 deleted file mode 100644 index 110969d5..00000000 --- a/tests/test_data/awg/TestAPS1/RabiAmp2/Rabi/Rabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:db62cca9c591811789eb181b4c0ddb0a904ce2dc7d05e7fb8050033daefd6ee0 -size 20568 diff --git a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 index df3425fa..2a6a3109 100644 --- a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8e2d323573f37158d606c7c0f87320f35749da11899da5758898df83216a1074 -size 2423 +oid sha256:6ac20319939b6dc7f0d64a1347a82bb9b671f767fe8b5e7a1bc06794d3d8cf77 +size 2425 diff --git a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.h5 deleted file mode 100644 index db4fa613..00000000 --- a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:757050fe27d6d6fcbfc2d9d4b6b02d09745ef75948e7012f8ffab9cc520bac99 -size 18520 diff --git a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 index 146d1267..82b0b907 100644 --- a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 +++ b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:ab58f521b56d072a3756047531e2397e0f48ae175465c4d4903f2b404dbd055d -size 1837 +oid sha256:9757b597ae5551218c1368ee3db777a3b411faed53fa7485d879b1c5a804856a +size 1839 diff --git a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.h5 b/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.h5 deleted file mode 100644 index b9c277eb..00000000 --- a/tests/test_data/awg/TestAPS1/RabiAmpPi/Rabi/Rabi-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea5abdf66a0317b1ac458e9a102a0f4feb1850f3f3f7114b310eee161804ee50 -size 18520 diff --git a/tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.h5 b/tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.h5 deleted file mode 100644 index 1c37d756..00000000 --- a/tests/test_data/awg/TestAPS1/RabiWidth/Rabi/Rabi-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6f450ffb720bb132c6db69679f24efaeace7ac3e377a089205025af9d1394f3b -size 6240 diff --git a/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 b/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 index bb82c73d..084ba0e7 100644 --- a/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:773ff3534bb9c217c3753d59126dadf35d736f6e8785756896756a3d316605da -size 2361 +oid sha256:6ed98961c6bcb69b90e43cfc6eb4369e94070691496236fd2ccf9df913fe0624 +size 2363 diff --git a/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.h5 b/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.h5 deleted file mode 100644 index 14734f5a..00000000 --- a/tests/test_data/awg/TestAPS1/Ramsey/Ramsey-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:3783ad1b01a211e762dc1f7ff450852792638d739caabe2a2e4f735ef195e95d -size 18520 diff --git a/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 b/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 index 26b8f04c..7bdf4f68 100644 --- a/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:8acf4b023fd7a4e2e7c983f7ebd2fd9eda870d5ffb5f2ef2ac37adb6fed1f2c6 -size 39819 +oid sha256:332f9f54ee51746281533a5c854afe85e924763bd80a5aa07d51a76fb16cfb63 +size 39821 diff --git a/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.h5 b/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.h5 deleted file mode 100644 index 73756191..00000000 --- a/tests/test_data/awg/TestAPS1/SPAM/SPAM-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:90c2eb18127ca1d8811d3d5cc3522f16f19748279ffc003d421b0181bdbd351f -size 56762 diff --git a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.h5 b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.h5 deleted file mode 100644 index 1c37d756..00000000 --- a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6f450ffb720bb132c6db69679f24efaeace7ac3e377a089205025af9d1394f3b -size 6240 diff --git a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.h5 b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.h5 deleted file mode 100644 index 1c37d756..00000000 --- a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:6f450ffb720bb132c6db69679f24efaeace7ac3e377a089205025af9d1394f3b -size 6240 diff --git a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2_py27.h5 b/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2_py27.h5 deleted file mode 100644 index 39bfc4da..00000000 --- a/tests/test_data/awg/TestAPS1/SimultaneousRB_AC/RB/RB-APS2_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:dcc7226c4d9e8f9f5e56794d7e057003af6eaaecd4c841fdef2fedc2bf9b45f1 -size 6240 diff --git a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 b/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 index fde6c83e..0b1b623e 100644 --- a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:07eddb3d9ee5da4bfada4c4bb18f2a0f3222475932c9a3a2c82a406e2238ebe9 -size 121305 +oid sha256:904e7c9a02ec999e6fc7aaa2f28a98384eb67a4a031319b5c799dbd2aa998be2 +size 121307 diff --git a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.h5 b/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.h5 deleted file mode 100644 index 128fe7e0..00000000 --- a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:18a60bc56608e36d178c60119cb9526ad4caa68f520aa8f5c2924db45342b31a -size 138680 diff --git a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1_py27.h5 b/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1_py27.h5 deleted file mode 100644 index 748f1796..00000000 --- a/tests/test_data/awg/TestAPS1/SingleQubitRB/RB/RB-APS1_py27.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:a322f8589452532de3f3d7feeefd1e58dbb83796250e37529c854deef24f1534 -size 138680 diff --git a/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 b/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 index 9ecd230b..4012d581 100644 --- a/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:93807ccf7f1e8b14170513bf895265808db74c490226e32e94d10b18c1ec9822 -size 1147 +oid sha256:13fc8118413ab1d83e6390cb06029e66aa126d33cc8404d92f0123da772705fb +size 1149 diff --git a/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.h5 b/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.h5 deleted file mode 100644 index 4f0f920c..00000000 --- a/tests/test_data/awg/TestAPS1/SingleShot/SingleShot-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:0c5996784d291c3dde24b082854188a5704304e70be802478a07ededf6993e92 -size 18520 diff --git a/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 b/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 index da311979..b4b0497d 100644 --- a/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:33985607d236dccec3424242fbfc3b21d51d64ed5037750565f0c89f671b2add -size 1107 +oid sha256:40da4a38af7ff4312e61f0055908cd6273beaa80be8fdd70581a8a2154ca12b1 +size 1109 diff --git a/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.h5 b/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.h5 deleted file mode 100644 index f4f6922a..00000000 --- a/tests/test_data/awg/TestAPS1/Spec/Spec-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:ea122c4f469e951782d29dc14dc83878dcad293e6e54ac284a3940186b32c30b -size 18520 diff --git a/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 b/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 index cae5aca6..44568cfc 100644 --- a/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 +++ b/tests/test_data/awg/TestAPS1/T1/T1-APS1.aps1 @@ -1,3 +1,3 @@ version https://git-lfs.github.com/spec/v1 -oid sha256:703d52f368fe7062f2aedd7bd5d2d2fc4b672971b75865a626e4384c0ebbcb68 -size 1907 +oid sha256:8613cbca09ccd2a961c84c340b50414cbdef98be371675f39ff64f31ced993da +size 1909 diff --git a/tests/test_data/awg/TestAPS1/T1/T1-APS1.h5 b/tests/test_data/awg/TestAPS1/T1/T1-APS1.h5 deleted file mode 100644 index 63c12621..00000000 --- a/tests/test_data/awg/TestAPS1/T1/T1-APS1.h5 +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:fd60ddb02bfb69b40191e2d93683ced7d77f409d160af71f489bb9f28e174651 -size 18520 diff --git a/utils/convert_h5_to_aps.py b/utils/convert_h5_to_aps.py index 138910a6..3b0eb8b6 100644 --- a/utils/convert_h5_to_aps.py +++ b/utils/convert_h5_to_aps.py @@ -5,22 +5,27 @@ def write_to_aps1(fileName, data): channelDataFor = np.array([i in data['channelDataFor'] for i in range(1,5)], dtype=np.bool) - with open(fileName, 'wb') as FID: - FID.write(b'APS1') # target hardware - FID.write(np.float32(2.2).tobytes()) # Version - FID.write(channelDataFor.tobytes()) # channelDataFor - FID.write(np.array(data['miniLLRepeat'], dtype=np.bool).tobytes()) # MiniLLRepeat - for name in data['channels'].keys(): - FID.write(np.uint8(data['channels'][name]['isIQMode']).tobytes()) # isIQMode - FID.write(np.uint64(data['channels'][name]['waveformLib'].size).tobytes()) # Length of waveforms - FID.write(data['channels'][name]['waveformLib'].tobytes()) # Waveforms np.int16 - for name in ['chan_1', 'chan_3']: - if 'linkListData' in data['channels'][name].keys(): - FID.write(np.uint64(data['channels'][name]['linkListNumKeys']).tobytes()) # numKeys - FID.write(np.uint64(data['channels'][name]['linkListDataLength']).tobytes()) # numEntries - for key, dataVec in data['channels'][name]['linkListData'].items(): - FID.write(key.ljust(32,"#").encode("utf-8")) # Key 32 byte utf-8 - FID.write(dataVec.tobytes()) + try: + with open(fileName, 'wb') as FID: + FID.write(b'APS1') # target hardware + FID.write(np.float32(2.2).tobytes()) # Version + FID.write(channelDataFor.tobytes()) # channelDataFor + FID.write(np.array(data['miniLLRepeat'], dtype=np.bool).tobytes()) # MiniLLRepeat + for name in data['channels'].keys(): + FID.write(np.uint8(data['channels'][name]['isIQMode']).tobytes()) # isIQMode + FID.write(np.uint64(data['channels'][name]['waveformLib'].size).tobytes()) # Length of waveforms + FID.write(data['channels'][name]['waveformLib'].tobytes()) # Waveforms np.int16 + FID.write(np.uint8('linkListData' in data['channels']['chan_1'].keys()).tobytes()) # LL for chan1 + FID.write(np.uint8('linkListData' in data['channels']["chan_3"].keys()).tobytes()) # LL for chan3 + for name in ['chan_1', 'chan_3']: + if 'linkListData' in data['channels'][name].keys(): + FID.write(np.uint64(data['channels'][name]['linkListNumKeys']).tobytes()) # numKeys + FID.write(np.uint64(data['channels'][name]['linkListDataLength']).tobytes()) # numEntries + for key, dataVec in data['channels'][name]['linkListData'].items(): + FID.write(key.ljust(32,"#").encode("utf-8")) # Key 32 byte utf-8 + FID.write(dataVec.tobytes()) + except: + print(f"Warning: could not write aps1 file {fileName}") def write_to_aps2(fileName, data): instructions = data['instructions'] @@ -78,7 +83,6 @@ def read_aps1_from_h5(fileName): for channel in channels: data['channels'][channel] = {'waveformLib': FID[f'/{channel}/waveformLib'].value.flatten()} data['channels'][channel]['isIQMode'] = FID[f'/{channel}'].attrs['isIQMode'] - # print(list(FID[f'/{channel}'].keys())) if 'linkListData' in list(FID[f'/{channel}'].keys()): data['channels'][channel]['linkListData'] = {} for key in FID[f'/{channel}/linkListData'].keys(): @@ -88,14 +92,15 @@ def read_aps1_from_h5(fileName): return data if __name__ == '__main__': - basename, ext = os.path.splitext(sys.argv[1]) - print(f"Converting {basename+'.h5'}") - inst = get_type(basename+'.h5') - if inst == "APS2": - data = read_aps2_from_h5(basename+'.h5') - write_to_aps2(basename+".aps2", data) - if inst == "APS1": - data = read_aps1_from_h5(basename+'.h5') - write_to_aps1(basename+".aps1", data) + for filename in sys.argv[1:]: + basename, ext = os.path.splitext(filename) + print(f"Converting {basename+'.h5'}") + inst = get_type(basename+'.h5') + if inst == "APS2": + data = read_aps2_from_h5(basename+'.h5') + write_to_aps2(basename+".aps2", data) + if inst == "APS1": + data = read_aps1_from_h5(basename+'.h5') + write_to_aps1(basename+".aps1", data) From 9db5fa05e0291deccff1adff85948e4ebbeb7196 Mon Sep 17 00:00:00 2001 From: qlab Date: Thu, 14 Feb 2019 15:59:14 -0500 Subject: [PATCH 107/235] Warn if existing object instead of error --- QGL/ChannelLibraries.py | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 8515aa5d..efd55ff7 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -41,6 +41,8 @@ import itertools import numpy as np import networkx as nx +from warnings import warn +import logging import bbndb @@ -55,6 +57,8 @@ channelLib = None +logger = logging.getLogger(__name__) + def check_session_dirty(f): """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" @wraps(f) @@ -75,7 +79,8 @@ def check_for_duplicates(f): @wraps(f) def wrapper(cls, label, *args, **kwargs): if label in cls.channelDict: - raise ValueError(f"Cannot create {label}: a channel with the same name already exists.") + logger.warning(f"Cannot create {label}: a channel with the same name already exists.") + return cls.channelDict[label] else: return f(cls, label, *args, **kwargs) return wrapper @@ -429,8 +434,10 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") if f"M-{qubit.label}" in self.channelDict: - raise ValueError(f"Cannot create Measurement M-{qubit.label}: a channel with the same name already exists.") - meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=self.channelDatabase) + logger.warning(f"Cannot create Measurement M-{qubit.label}: a channel with the same name already exists.") + meas = self.channelDict[f"M-{qubit.label}"] + else: + meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=self.channelDatabase) meas.phys_chan = phys_chan if generator: meas.phys_chan.generator = generator From c54941d751ab19987073e0e2eb45d37c46e1a257 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Thu, 14 Feb 2019 16:35:10 -0500 Subject: [PATCH 108/235] Unnecessary warn import --- QGL/ChannelLibraries.py | 1 - 1 file changed, 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index efd55ff7..307cbbf3 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -41,7 +41,6 @@ import itertools import numpy as np import networkx as nx -from warnings import warn import logging import bbndb From 0f92d035581ee7f94931d286a0e9900a2419f47a Mon Sep 17 00:00:00 2001 From: gribeill Date: Thu, 14 Feb 2019 17:53:02 -0500 Subject: [PATCH 109/235] Initial commit of changes for hardware randomization --- QGL/Compiler.py | 36 +++++++++++++++++++++++++++++++++++- QGL/PatternUtils.py | 5 +++++ QGL/PulsePrimitives.py | 15 +++++++++++++-- QGL/PulseSequencer.py | 9 ++++++--- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 115338de..fc240556 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -30,6 +30,7 @@ from . import Channels from . import ChannelLibraries from . import PulseShapes +from . import PulsePrimitives from .PulsePrimitives import Id, clear_pulse_cache from .PulseSequencer import Pulse, PulseBlock, CompositePulse from . import ControlFlow @@ -221,6 +222,10 @@ def generate_waveforms(physicalWires): for pulse in flatten(wire): if not isinstance(pulse, Pulse): continue + if pulse.isRunTime: + #skip run-time pulses as we need to add the set of all possible pulses to + #the waveform table + continue if pulse.hashshape() not in wfs[ch]: if pulse.isTimeAmp: wfs[ch][pulse.hashshape()] = np.ones(1, dtype=np.complex) @@ -228,6 +233,24 @@ def generate_waveforms(physicalWires): wfs[ch][pulse.hashshape()] = pulse.shape return wfs +def add_runtime_pulse_set(physicalWires, wfs): + + for ch, wire in physicalWires.items(): + rt_pulses = [pulse for pulse in flatten(wire) if pulse.isRunTime] + pulse_type = set([pulse.label for pulse in rt_pulses]) + if len(pulse_type) > 1: + raise Exception("All run-time pulses must have the same label.") + if pulse_type not in ("RandomAC, RandomDiAC"): + raise Exception(f"Unknown run time pulse type {pulse_type}.") + + pulse_fn = getattr(PulsePrimitives, pulse_type.split('Random')[-1], None) + + + + + + + def pulses_to_waveforms(physicalWires): logger.debug("Converting pulses_to_waveforms:") @@ -426,6 +449,10 @@ def compile_to_hardware(seqs, logger.info("Generating waveform library.") wfs = generate_waveforms(physWires) + # If there are run-time pulses add these to the library + if PatternUtils.contains_runtime_pulses(seqs): + wfs = add_runtime_pulse_set(physWires, wfs) + # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") physWires = pulses_to_waveforms(physWires) @@ -712,7 +739,8 @@ class Waveform(object): #Use slots to create attributes to save on memory. __slots__ = ["label", "key", "amp", "length", "phase", "frameChange", - "isTimeAmp", "frequency", "logicalChan", "maddr", "startTime"] + "isTimeAmp", "frequency", "logicalChan", "maddr", "startTime", + "isRunTime"] def __init__(self, pulse=None): if pulse is None: @@ -726,6 +754,7 @@ def __init__(self, pulse=None): self.frequency = 0 self.logicalChan = "" self.maddr = (-1, 0) + self.isRunTime = False else: self.label = pulse.label self.key = pulse.hashshape() @@ -737,11 +766,14 @@ def __init__(self, pulse=None): self.frequency = pulse.frequency self.logicalChan = pulse.channel self.maddr = pulse.maddr + self.isRunTime = pulse.isRunTime def __repr__(self): return self.__str__() def __str__(self): + if self.isRunTime: + return f"Hardware-Generated({self.length})" if self.isTimeAmp: TA = 'HIGH' if self.amp != 0 else 'LOW' return "Waveform-TA(" + TA + ", " + str(self.length) + ")" @@ -750,6 +782,8 @@ def __str__(self): self.key)[:6] + ", " + str(self.length) + ")" def __eq__(self, other): + if self.isRunTime or other.isRunTime: + return False if isinstance(other, self.__class__): #No __dict__ property so we check all properties. return all((getattr(self, attr, None) == getattr(other, attr, None) for attr in self.__slots__)) diff --git a/QGL/PatternUtils.py b/QGL/PatternUtils.py index 9298d45e..0719a3e0 100644 --- a/QGL/PatternUtils.py +++ b/QGL/PatternUtils.py @@ -208,6 +208,11 @@ def add_digitizer_trigger(seqs): trig_chan in seq[ct].pulses.keys()): seq[ct] = align('left', seq[ct], TAPulse("TRIG", trig_chan, trig_chan.pulse_params['length'], 1.0, 0.0, 0.0)) +def contains_runtime_pulses(seqs): + """ + Determines if sequences contain run-time generated pulses. + """ + return any([hasattr(entry, 'isRunTime') and entry.isRunTime for entry in flatten(seqs)]) def contains_measurement(entry): """ diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index bfbf03b2..1ad4fbbe 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -25,12 +25,15 @@ from functools import wraps, reduce -def overrideDefaults(chan, updateParams): +def overrideDefaults(chan, updateParams, ignoredStrParams=['isRunTime', 'maddr', 'moffset']): '''Helper function to update any parameters passed in and fill in the defaults otherwise.''' # The default parameter list depends on the channel type so pull out of channel # Then update passed values paramDict = chan.pulse_params.copy() paramDict.update(updateParams) + for param in ignoredStrParams: + if param in paramDict.keys(): + paramDict.pop(param) return paramDict @@ -113,7 +116,7 @@ def Utheta(qubit, else: # linearly scale based upon the 'pi/2' amplitude amp = (angle / (pi/2)) * qubit.pulse_params['pi2Amp'] - return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency) + return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency, **kwargs) # generic pulses around X, Y, and Z axes @@ -337,6 +340,14 @@ def arb_axis_drag(qubit, return Pulse(kwargs["label"] if "label" in kwargs else "ArbAxis", qubit, params, 1.0, aziAngle, frameChange) +def RandomAC(qubit): + params = overrideDefaults(qubit, {}) + return Pulse("RandomAC", qubit, params, isRunTime=True) + +def RandomDiAC(qubit): + params = overrideDefaults(qubit, {}) + params["length"] *= 2.0 + return Pulse("RandomAC", qubit, params, isRunTime=True) def AC(qubit, cliffNum): """ diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 315c3db8..606511dd 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -33,10 +33,11 @@ class Pulse(namedtuple("Pulse", ["label", "channel", "length", "amp", "phase", "frequency", "frameChange", "shapeParams", "isTimeAmp", "isZero", "ignoredStrParams", - "maddr", "moffset"])): + "maddr", "moffset", "isRunTime"])): __slots__ = () - def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, ignoredStrParams=[], maddr=-1, moffset=0, frequency=None): + def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, + ignoredStrParams=[], maddr=-1, moffset=0, frequency=None, isRunTime=False): if frequency: frequency = frequency elif hasattr(channel, 'frequency'): @@ -48,12 +49,14 @@ def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, i if param not in shapeParams.keys(): raise NameError("shapeParams must include {0}".format(param)) isTimeAmp = (shapeParams['shape_fun'] == PulseShapes.constant) + if isRunTime: + isTimeAmp = False isZero = (amp == 0) return super(cls, Pulse).__new__(cls, label, channel, shapeParams['length'], amp, phase, frequency, frameChange, shapeParams, isTimeAmp, isZero, ignoredStrParams, - maddr, moffset) + maddr, moffset, isRunTime) def __str__(self): kwvals = [] From 60bdf94efcc40b06b6145a1e9a77bb3c28535469 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 15 Feb 2019 15:08:42 -0500 Subject: [PATCH 110/235] new_APS convenience function --- QGL/ChannelLibraries.py | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 307cbbf3..08d4bab7 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -305,6 +305,22 @@ def new_APS2(self, label, address): self.add_and_update_dict(this_transmitter) return this_transmitter + @check_for_duplicates + def new_APS(self, label, address): + chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + chan2 = Channels.PhysicalQuadratureChannel(label=f"{label}-34", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-1m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-2m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-2m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-4m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + + this_transmitter = Channels.Transmitter(label=label, model="APS", address=address, channels=[chan1, chan2, m1, m2, m3, m4], channel_db=self.channelDatabase) + this_transmitter.trigger_source = "external" + this_transmitter.address = address + + self.add_and_update_dict(this_transmitter) + return this_transmitter + @check_for_duplicates def new_TDM(self, label, address): return Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6) From 0b807e44e71effd9a0ac47206ecf9698fdd0586d Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 15 Feb 2019 15:08:57 -0500 Subject: [PATCH 111/235] Delete future --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index c4c6ff95..845ac4e2 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -20,7 +20,7 @@ import logging from warnings import warn from copy import copy -from future.moves.itertools import zip_longest +from itertools import zip_longest import pickle import struct From 63b23f3fc2627cf2dc921267998beeae279655d6 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Fri, 15 Feb 2019 16:05:28 -0500 Subject: [PATCH 112/235] Use temporary directory for test_sequence data is AWG dir is not specified --- tests/test_Sequences.py | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index 9cb343b8..d9eb1ee8 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -1,5 +1,6 @@ import numpy as np import unittest, time, os, random, sys +import tempfile from QGL import * from QGL import GSTTools @@ -103,20 +104,19 @@ def get_qubits(self): for name in self.qubit_names] def set_awg_dir(self, footer=""): - cn = self.__class__.__name__ - - if None == QGL.config.AWGDir: - logger.warning( "\n\r#----- EEE NULL QGL.config.AWGDir {%s} cited; calling load_config()...", QGL.config.AWGDir) + if QGL.config.AWGDir is None: QGL.config.load_config() - logger.warning( "#----- Post load-config QGL.config.AWGDir: {%s}.\n\r", QGL.config.AWGDir) - #else: - # logger.warning( "\n\r#----- Using QGL.config.AWGDir {%s} 8-p", QGL.config.AWGDir) + + if QGL.config.AWGDir is None: + self.temp_dir = tempfile.TemporaryDirectory() + QGL.config.AWGDir = self.temp_dir.name + logger.warning(f"Creating temporary AWG dir at {QGL.config.AWGDir}") + if not hasattr(self, 'original_awg_dir'): self.original_awg_dir = QGL.config.AWGDir - self.awg_dir = os.path.abspath(self.original_awg_dir + os.path.sep + cn) - self.truth_dir = os.path.abspath(self.testFileDirectory + os.path.sep + - cn) + self.awg_dir = os.path.abspath(self.original_awg_dir + os.path.sep + self.__class__.__name__) + self.truth_dir = os.path.abspath(self.testFileDirectory + os.path.sep + self.__class__.__name__) if footer != "": self.awg_dir = self.awg_dir + os.path.sep + footer From ea178ec360cf93390dc6f1fecfcc2a6ca9373ab5 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 18 Feb 2019 17:12:17 -0500 Subject: [PATCH 113/235] WIP --- QGL.egg-info/PKG-INFO | 10 +++++++ QGL.egg-info/SOURCES.txt | 41 +++++++++++++++++++++++++++++ QGL.egg-info/dependency_links.txt | 1 + QGL.egg-info/requires.txt | 6 +++++ QGL.egg-info/top_level.txt | 1 + QGL/Compiler.py | 43 +++++++++++++++++++++---------- QGL/PulsePrimitives.py | 2 +- 7 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 QGL.egg-info/PKG-INFO create mode 100644 QGL.egg-info/SOURCES.txt create mode 100644 QGL.egg-info/dependency_links.txt create mode 100644 QGL.egg-info/requires.txt create mode 100644 QGL.egg-info/top_level.txt diff --git a/QGL.egg-info/PKG-INFO b/QGL.egg-info/PKG-INFO new file mode 100644 index 00000000..ac9daf85 --- /dev/null +++ b/QGL.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: QGL +Version: 2.1 +Summary: UNKNOWN +Home-page: https://github.com/BBN-Q/QGL +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/QGL.egg-info/SOURCES.txt b/QGL.egg-info/SOURCES.txt new file mode 100644 index 00000000..945e4a89 --- /dev/null +++ b/QGL.egg-info/SOURCES.txt @@ -0,0 +1,41 @@ +README.md +QGL/BlockLabel.py +QGL/ChannelLibraries.py +QGL/Channels.py +QGL/Cliffords.py +QGL/Compiler.py +QGL/ControlFlow.py +QGL/GSTTools.py +QGL/PatternUtils.py +QGL/Plotting.py +QGL/PulsePrimitives.py +QGL/PulseSequencePlotter.py +QGL/PulseSequencer.py +QGL/PulseShapes.py +QGL/Scheduler.py +QGL/TdmInstructions.py +QGL/Tomography.py +QGL/__init__.py +QGL/config.py +QGL/config_location.py +QGL/mm.py +QGL.egg-info/PKG-INFO +QGL.egg-info/SOURCES.txt +QGL.egg-info/dependency_links.txt +QGL.egg-info/requires.txt +QGL.egg-info/top_level.txt +QGL/BasicSequences/AllXY.py +QGL/BasicSequences/BlankingSweeps.py +QGL/BasicSequences/CR.py +QGL/BasicSequences/Decoupling.py +QGL/BasicSequences/Feedback.py +QGL/BasicSequences/FlipFlop.py +QGL/BasicSequences/RB.py +QGL/BasicSequences/Rabi.py +QGL/BasicSequences/SPAM.py +QGL/BasicSequences/T1T2.py +QGL/BasicSequences/__init__.py +QGL/BasicSequences/helpers.py +QGL/drivers/APS2Pattern.py +QGL/drivers/APSPattern.py +QGL/drivers/__init__.py \ No newline at end of file diff --git a/QGL.egg-info/dependency_links.txt b/QGL.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/QGL.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/QGL.egg-info/requires.txt b/QGL.egg-info/requires.txt new file mode 100644 index 00000000..2aa45f83 --- /dev/null +++ b/QGL.egg-info/requires.txt @@ -0,0 +1,6 @@ +bbndb>=0.1 +numpy>=1.11.1 +scipy>=0.17.1 +networkx>=1.11 +bqplot>=0.11.5 +sqlalchemy>=1.2.17 diff --git a/QGL.egg-info/top_level.txt b/QGL.egg-info/top_level.txt new file mode 100644 index 00000000..0e533603 --- /dev/null +++ b/QGL.egg-info/top_level.txt @@ -0,0 +1 @@ +QGL diff --git a/QGL/Compiler.py b/QGL/Compiler.py index fc240556..db3fc543 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -234,23 +234,40 @@ def generate_waveforms(physicalWires): return wfs def add_runtime_pulse_set(physicalWires, wfs): - for ch, wire in physicalWires.items(): - rt_pulses = [pulse for pulse in flatten(wire) if pulse.isRunTime] - pulse_type = set([pulse.label for pulse in rt_pulses]) + rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] + if len(rt_pulses) == 0: + continue + pulse_type = list(set([pulse.label for pulse in rt_pulses])) + pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) + if len(pulse_type) > 1: - raise Exception("All run-time pulses must have the same label.") - if pulse_type not in ("RandomAC, RandomDiAC"): + raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") + if len(pulse_logical_chan) > 1: + raise Exception(f"Found more than one channel per run-time pulse set: {pulse_logical_chan}.") + if pulse_type[0] not in ("RandomAC, RandomDiAC"): raise Exception(f"Unknown run time pulse type {pulse_type}.") + pulse_fn = getattr(PulsePrimitives, pulse_type[0].split('Random')[-1], None) + if pulse_fn is None: + raise Exception("Was unable to get pulse type for run-time generated pulses.") + + all_cliffords = [pulse_fn(pulse_logical_chan[0], n) for n in range(24)] + #Some could be CompositePulses so crack into indvidual pulses + all_pulses = [] + for cliff in all_cliffords: + try: + for p in cliff.pulses: + all_pulses.append(p) + except AttributeError: + all_pulses.append(cliff) + for cp in all_pulses: + if cp.hashshape() not in wfs[ch]: #don't duplicate data + if cp.isTimeAmp: + wfs[ch][cp.hashshape()] = np.ones(1, dtype=np.complex) + else: + wfs[ch][cp.hashshape()] = cp.shape - pulse_fn = getattr(PulsePrimitives, pulse_type.split('Random')[-1], None) - - - - - - - + return wfs def pulses_to_waveforms(physicalWires): logger.debug("Converting pulses_to_waveforms:") diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 1ad4fbbe..596c79d2 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -347,7 +347,7 @@ def RandomAC(qubit): def RandomDiAC(qubit): params = overrideDefaults(qubit, {}) params["length"] *= 2.0 - return Pulse("RandomAC", qubit, params, isRunTime=True) + return Pulse("RandomDiAC", qubit, params, isRunTime=True) def AC(qubit, cliffNum): """ From 0dae2942dbd6362c62739b7d8df768c7a5376c8b Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 19 Feb 2019 16:55:03 -0500 Subject: [PATCH 114/235] Fix APS1 marker name --- QGL/ChannelLibraries.py | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 08d4bab7..3bdc2cb8 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -182,18 +182,6 @@ def qubits(self): def meas(self): return self.ent_by_type(Channels.Measurement) - def ls_receivers(self): - return self.ent_by_type(Channels.Receiver, show=True) - - def ls_transmitters(self): - return self.ent_by_type(Channels.Transmitter, show=True) - - def ls_qubits(self): - return self.ent_by_type(Channels.Qubit, show=True) - - def ls_measurements(self): - return self.ent_by_type(Channels.Measurement, show=True) - @check_session_dirty def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ @@ -311,7 +299,7 @@ def new_APS(self, label, address): chan2 = Channels.PhysicalQuadratureChannel(label=f"{label}-34", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) m1 = Channels.PhysicalMarkerChannel(label=f"{label}-1m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) m2 = Channels.PhysicalMarkerChannel(label=f"{label}-2m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) - m3 = Channels.PhysicalMarkerChannel(label=f"{label}-2m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-3m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) m4 = Channels.PhysicalMarkerChannel(label=f"{label}-4m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) this_transmitter = Channels.Transmitter(label=label, model="APS", address=address, channels=[chan1, chan2, m1, m2, m3, m4], channel_db=self.channelDatabase) From afbe77c0fb2b0f5a091474b3b0106689c2f390f8 Mon Sep 17 00:00:00 2001 From: qlab Date: Wed, 20 Feb 2019 14:42:31 -0500 Subject: [PATCH 115/235] Added compatibility for using APS1 --- QGL/ChannelLibraries.py | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 3bdc2cb8..daf74fe8 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -389,12 +389,16 @@ def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, refere self.add_and_update_dict(thing) return thing - def set_control(self, qubit_or_edge, transmitter, generator=None): + def set_control(self, qubit_or_edge, transmitter, generator=None, aps1_chan=None): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: - raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + #raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + print("Warning: In set_control the Transmitter must have a single quadrature channel if using APS2. Assuming APS1.") + if aps1_chan != '12' and aps1_chan != '34': + raise ValueError("Assuming APS1, specify APS channel 12 or 34") + phys_chan = transmitter.ch(aps1_chan) elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: phys_chan = quads[0] elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): @@ -423,12 +427,16 @@ def set_qubit_connectivity(self, graph): self.add_and_update_dict(new_edges) return new_edges - def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): + def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7, aps1_chan=None): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: - raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + #raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + print("Warning: In set_measure the Transmitter must have a single quadrature channel if using APS2. Assuming APS1.") + if aps1_chan != '12' and aps1_chan != '34': + raise ValueError("Assuming APS1, specify APS channel 12 or 34") + phys_chan = transmitter.ch(aps1_chan) elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: phys_chan = quads[0] elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): From 3a8de165cd0132f500921245417b94a681719593 Mon Sep 17 00:00:00 2001 From: qlab Date: Wed, 20 Feb 2019 15:28:56 -0500 Subject: [PATCH 116/235] Better fix for APS1 compatibility --- QGL/ChannelLibraries.py | 44 +++++++++++++++++++---------------------- 1 file changed, 20 insertions(+), 24 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index daf74fe8..e9209259 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -389,20 +389,18 @@ def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, refere self.add_and_update_dict(thing) return thing - def set_control(self, qubit_or_edge, transmitter, generator=None, aps1_chan=None): - quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] - markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] - - if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: - #raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - print("Warning: In set_control the Transmitter must have a single quadrature channel if using APS2. Assuming APS1.") - if aps1_chan != '12' and aps1_chan != '34': - raise ValueError("Assuming APS1, specify APS channel 12 or 34") - phys_chan = transmitter.ch(aps1_chan) - elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: - phys_chan = quads[0] + def set_control(self, qubit_or_edge, transmitter, generator=None): + + if isinstance(transmitter, Channels.Transmitter): + quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + if len(quads) > 1: + raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + elif len(quads) == 1: + phys_chan = quads[0] elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): phys_chan = transmitter + markers = [c for c in transmitter.transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] else: raise ValueError("In set_control the Transmitter must have a single quadrature channel or a specific channel must be passed instead") @@ -427,20 +425,18 @@ def set_qubit_connectivity(self, graph): self.add_and_update_dict(new_edges) return new_edges - def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7, aps1_chan=None): - quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] - markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] - - if isinstance(transmitter, Channels.Transmitter) and len(quads) > 1: - #raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - print("Warning: In set_measure the Transmitter must have a single quadrature channel if using APS2. Assuming APS1.") - if aps1_chan != '12' and aps1_chan != '34': - raise ValueError("Assuming APS1, specify APS channel 12 or 34") - phys_chan = transmitter.ch(aps1_chan) - elif isinstance(transmitter, Channels.Transmitter) and len(quads) == 1: - phys_chan = quads[0] + def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channel=None, gate=False, gate_channel=None, trigger_length=1e-7): + + if isinstance(transmitter, Channels.Transmitter): + quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] + markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] + if len(quads) > 1: + raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + elif len(quads) == 1: + phys_chan = quads[0] elif isinstance(transmitter, Channels.PhysicalQuadratureChannel): phys_chan = transmitter + markers = [c for c in transmitter.transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] else: raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") From dbc8fab57e265fe3945dff01d02e55667ddd306f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Thu, 21 Feb 2019 10:54:30 -0500 Subject: [PATCH 117/235] Add DC source as a standalone instrument --- QGL/ChannelLibraries.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 307cbbf3..248d95c4 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -313,6 +313,10 @@ def new_TDM(self, label, address): def new_spectrum_analzyer(self, label, address, source): return Channels.SpectrumAnalyzer(label=label, model="SpectrumAnalyzer", address=address, LO_source=source) + @check_for_duplicates + def new_DC_source(self, label, address): + return Channels.DCSource(label=label, model="YokogawaGS200", address=address, standalone=True) + @check_for_duplicates def new_APS2_rack(self, label, ip_addresses, tdm_ip=None): transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] From 3338aad546c39dafad44462adea83b1df08fb2db Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 22 Feb 2019 17:35:26 -0500 Subject: [PATCH 118/235] WIP --- QGL/Compiler.py | 122 ++++++++++++++++++++++--------------- QGL/ControlFlow.py | 3 +- QGL/PulseSequencer.py | 5 ++ QGL/TdmInstructions.py | 4 +- QGL/drivers/APS2Pattern.py | 28 ++++----- 5 files changed, 95 insertions(+), 67 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index db3fc543..bdf3ab2e 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -233,34 +233,40 @@ def generate_waveforms(physicalWires): wfs[ch][pulse.hashshape()] = pulse.shape return wfs +def get_clifford_type(wire, allowed_types=("RandomAC")): + rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] + if len(rt_pulses) == 0: + return None + pulse_type = list(set([pulse.label for pulse in rt_pulses])) + pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) + if len(pulse_type) > 1: + raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") + if len(pulse_logical_chan) > 1: + raise Exception(f"Found more than one channel per run-time pulse set: {pulse_logical_chan}.") + if pulse_type[0] not in allowed_types: + raise Exception(f"Unknown run time pulse type {pulse_type}.") + pulse_fn = getattr(PulsePrimitives, pulse_type[0].split('Random')[-1], None) + if pulse_fn is None: + raise Exception("Was unable to get pulse type for run-time generated pulses.") + return lambda x: pulse_fn(pulse_logical_chan[0], x) + + def add_runtime_pulse_set(physicalWires, wfs): for ch, wire in physicalWires.items(): - rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] - if len(rt_pulses) == 0: - continue - pulse_type = list(set([pulse.label for pulse in rt_pulses])) - pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) - - if len(pulse_type) > 1: - raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") - if len(pulse_logical_chan) > 1: - raise Exception(f"Found more than one channel per run-time pulse set: {pulse_logical_chan}.") - if pulse_type[0] not in ("RandomAC, RandomDiAC"): - raise Exception(f"Unknown run time pulse type {pulse_type}.") - pulse_fn = getattr(PulsePrimitives, pulse_type[0].split('Random')[-1], None) + pulse_fn = get_clifford_type(wire) if pulse_fn is None: - raise Exception("Was unable to get pulse type for run-time generated pulses.") - - all_cliffords = [pulse_fn(pulse_logical_chan[0], n) for n in range(24)] - #Some could be CompositePulses so crack into indvidual pulses + continue all_pulses = [] + all_cliffords = [pulse_fn(n) for n in range(24)] + #Some could be CompositePulses so crack into indvidual pulses for cliff in all_cliffords: try: for p in cliff.pulses: all_pulses.append(p) except AttributeError: all_pulses.append(cliff) - for cp in all_pulses: + + for cp in all_pulses: #make all-pulses a set? if cp.hashshape() not in wfs[ch]: #don't duplicate data if cp.isTimeAmp: wfs[ch][cp.hashshape()] = np.ones(1, dtype=np.complex) @@ -269,6 +275,15 @@ def add_runtime_pulse_set(physicalWires, wfs): return wfs +def create_clifford_waveforms(physicalWires): + clifford_sets = {} + for ch, wire in physicalWires.items(): + pulse_fn = get_clifford_type(wire) + if pulse_fn is None: + continue + clifford_sets[ch] = [[Waveform(pulse_fn(n))] for n in range(24)] + return clifford_sets + def pulses_to_waveforms(physicalWires): logger.debug("Converting pulses_to_waveforms:") wireOuts = {ch: [] for ch in physicalWires.keys()} @@ -312,7 +327,7 @@ def setup_awg_channels(physicalChannels): return data -def bundle_wires(physWires, wfs): +def bundle_wires(physWires, wfs, cliff_wires=None): awgData = setup_awg_channels(physWires.keys()) for chan in physWires.keys(): _, awgChan = chan.label.rsplit('-', 1) @@ -320,6 +335,8 @@ def bundle_wires(physWires, wfs): awgChan = 'ch' + awgChan awgData[chan.instrument][awgChan]['linkList'] = physWires[chan] awgData[chan.instrument][awgChan]['wfLib'] = wfs[chan] + if cliff_wires is not None and chan in cliff_wires.keys(): + awgData[chan.instrument][awgChan]['clifford_set'] = cliff_wires[chan] if hasattr(chan, 'correctionT'): awgData[chan.instrument][awgChan]['correctionT'] = chan.correctionT return awgData @@ -340,37 +357,12 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_hardware(seqs, - fileName, - library_version=None, - suffix='', - axis_descriptor=None, - add_slave_trigger=True, - extra_meta=None, - tdm_seq = False): - ''' - Compiles 'seqs' to a hardware description and saves it to 'fileName'. - Other inputs: - library_version (optional): string or ChannelLibrary instance to pack in the - metafile. This will be the version of the library loaded during program - execution. Default None uses the current working version. - suffix (optional): string to append to end of fileName, e.g. with - fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 - axis_descriptor (optional): a list of dictionaries describing the effective - axes of the measurements that the sequence will yield. For instance, - if `seqs` generates a Ramsey experiment, axis_descriptor would describe - the time delays between pulses. - add_slave_trigger (optional): add the slave trigger(s) - tdm_seq (optional): compile for TDM - ''' +def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): + ChannelLibraries.channelLib.update_channelDict() clear_pulse_cache() - logger.debug("Compiling %d sequence(s)", len(seqs)) - # save input code to file - save_code(seqs, fileName + suffix) - # all sequences should start with a WAIT for synchronization for seq in seqs: if not isinstance(seq[0], ControlFlow.Wait): @@ -469,6 +461,9 @@ def compile_to_hardware(seqs, # If there are run-time pulses add these to the library if PatternUtils.contains_runtime_pulses(seqs): wfs = add_runtime_pulse_set(physWires, wfs) + cliff_wires = create_clifford_waveforms(physWires) + else: + cliff_wires = None # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") @@ -477,9 +472,36 @@ def compile_to_hardware(seqs, # bundle wires on instruments, or channels depending # on whether we have one sequence per channel logger.info("Bundling wires.") - awgData = bundle_wires(physWires, wfs) - del wireSeqs - gc.collect() + awgData = bundle_wires(physWires, wfs, cliff_wires=cliff_wires) + return awgData + +def compile_to_hardware(seqs, + fileName, + library_version=None, + suffix='', + axis_descriptor=None, + add_slave_trigger=True, + extra_meta=None, + tdm_seq = False): + ''' + Compiles 'seqs' to a hardware description and saves it to 'fileName'. + Other inputs: + library_version (optional): string or ChannelLibrary instance to pack in the + metafile. This will be the version of the library loaded during program + execution. Default None uses the current working version. + suffix (optional): string to append to end of fileName, e.g. with + fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 + axis_descriptor (optional): a list of dictionaries describing the effective + axes of the measurements that the sequence will yield. For instance, + if `seqs` generates a Ramsey experiment, axis_descriptor would describe + the time delays between pulses. + add_slave_trigger (optional): add the slave trigger(s) + tdm_seq (optional): compile for TDM + ''' + # save input code to file + save_code(seqs, fileName + suffix) + + awgData = compile_to_IR(seqs, add_slave_trigger=add_slave_trigger, tdm_seq=tdm_seq) # convert to hardware formats # files = {} @@ -790,7 +812,7 @@ def __repr__(self): def __str__(self): if self.isRunTime: - return f"Hardware-Generated({self.length})" + return f"Hardware-Generated({self.label}, {self.length})" if self.isTimeAmp: TA = 'HIGH' if self.amp != 0 else 'LOW' return "Waveform-TA(" + TA + ", " + str(self.length) + ")" diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index a735f24e..f733cb6a 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -150,8 +150,9 @@ def __init__(self, target): class Call(ControlInstruction): # target is a BlockLabel - def __init__(self, target): + def __init__(self, target, load_addr=False): super(Call, self).__init__("CALL", target=target) + self.load_addr = load_addr class Return(ControlInstruction): diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 606511dd..463b273b 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -262,6 +262,11 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + @property + def isRunTime(self): + #Check if any of the member pulses contain a run-time pulse + return any([hasattr(entry, 'isRunTime') and entry.isRunTime for entry in self.pulses.values()]) + @property def channel(self): return self.pulses.keys() diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index 945743c9..38fa7cc7 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -109,5 +109,5 @@ def __ne__(self, other): return not self == other -# def LoadCmpVram(addr, mask): -# return LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, mask) +def LoadCmpVram(addr, mask, tdm=True): + return LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, mask, tdm) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index c4c6ff95..5e6db0d7 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -31,6 +31,8 @@ from QGL import PulseSequencer from QGL.PatternUtils import hash_pulse, flatten from QGL import TdmInstructions +from QGL import APS2CustomInstructions +from QGL.APS2CustomInstructions import APS2_CUSTOM_DECODE # Python 2/3 compatibility: use 'int' that subclasses 'long' from builtins import int @@ -85,20 +87,17 @@ CMPTABLE = {'==': EQUAL, '!=': NOTEQUAL, '>': GREATERTHAN, '<': LESSTHAN} -# custom OP_CODES +# custom TDM OP_CODES TDM_MAJORITY_VOTE = 0 TDM_MAJORITY_VOTE_SET_MASK = 1 TDM_TSM_SET_ROUNDS = 2 TDM_TSM = 3 -APS_CUSTOM_DECODE = ["APS_RAND", "APS_CLIFFORD_RAND","APS_CLIFFORD_SET_SEED" ,"APS_CLIFFORD_SET_OFFSET" -, "APS_CLIFFORD_SET_SPACING"] - TDM_CUSTOM_DECODE = ["TDM_MAJORITY_VOTE", "TDM_MAJORITY_VOTE_SET_MASK", "TDM_TSM_SET_ROUNDS", "TDM_TSM"] # Whether we use PHASE_OFFSET modulation commands or bake it into waveform # Default to false as we usually don't have many variants -USE_PHASE_OFFSET_INSTRUCTION = False +USE_PHASE_OFFSET_INSTRUCTION = True # Whether to save the waveform offsets for partial compilation SAVE_WF_OFFSETS = False @@ -445,8 +444,8 @@ def Goto(addr, label=None): return Command(GOTO, addr, label=label) -def Call(addr, label=None): - return Command(CALL, addr, label=label) +def Call(addr, load_addr=False, label=None): + return Command(CALL, addr, label=label, write=load_addr) def Return(label=None): @@ -512,11 +511,11 @@ def LoadCmpVram(addr, mask, label=None): return Instruction(header, payload, label=label) -def preprocess(seqs, shapeLib): +def preprocess(seqs, shapeLib, clifford_set=False): seqs = PatternUtils.convert_lengths_to_samples( seqs, SAMPLING_RATE, ADDRESS_UNIT, Compiler.Waveform) wfLib = build_waveforms(seqs, shapeLib) - inject_modulation_cmds(seqs) + inject_modulation_cmds(seqs, force_phase_update=clifford_set) return seqs, wfLib @@ -604,7 +603,7 @@ def to_instruction(self, write_flag=True, label=None): instr.writeFlag = write_flag return instr -def inject_modulation_cmds(seqs): +def inject_modulation_cmds(seqs, force_phase_update=False): """ Inject modulation commands from phase, frequency and frameChange of waveforms in an IQ waveform sequence. Assume up to 2 NCOs for now. @@ -622,7 +621,7 @@ def inject_modulation_cmds(seqs): frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] no_frame_cmds = np.all(np.less(np.abs(frame_changes), 1e-8)) no_modulation_cmds = no_freq_cmds and no_phase_cmds and no_frame_cmds - + #import pdb; pdb.set_trace() if no_modulation_cmds: continue @@ -665,10 +664,11 @@ def inject_modulation_cmds(seqs): mod_seq.append( ModulationCommand("MODULATE", nco_select, length = entry.length)) pending_frame_update = False #now apply non-zero frame changes after so it is applied at end + if entry.frameChange != 0: pending_frame_update = True #zero length frame changes (Z pulses) need to be combined with the previous frame change or injected where possible - if entry.length == 0: + if entry.length == 0 and not force_phase_update: #if the last is a frame change then we can add to the frame change if isinstance(mod_seq[-1], ModulationCommand) and mod_seq[-1].instruction == "UPDATE_FRAME": mod_seq[-1].phase += entry.frameChange @@ -801,7 +801,7 @@ def create_seq_instructions(seqs, offsets, label = None): isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction) or - isinstance(entry, TdmInstructions.WriteAddrInstruction) or + isinstance(entry, TdmInstructions.Instruction) or isinstance(entry, TdmInstructions.LoadCmpVramInstruction)): if isinstance(entry, BlockLabel.BlockLabel): # carry label forward to next entry @@ -820,7 +820,7 @@ def create_seq_instructions(seqs, offsets, label = None): elif isinstance(entry, ControlFlow.Goto): instructions.append(Goto(entry.target, label=label)) elif isinstance(entry, ControlFlow.Call): - instructions.append(Call(entry.target, label=label)) + instructions.append(Call(entry.target, load_addr = entry.load_addr, label=label)) elif isinstance(entry, ControlFlow.Repeat): instructions.append(Repeat(entry.target, label=label)) # value argument commands From 99d9f3d34d01ece665c7829faf3f4f7dee257327 Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 22 Feb 2019 17:35:48 -0500 Subject: [PATCH 119/235] More WIP... --- QGL/APS2CustomInstructions.py | 74 +++++++++++++++++++++++++++++++++++ QGL/RandomCliffordTools.py | 51 ++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 QGL/APS2CustomInstructions.py create mode 100644 QGL/RandomCliffordTools.py diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py new file mode 100644 index 00000000..cb082dc0 --- /dev/null +++ b/QGL/APS2CustomInstructions.py @@ -0,0 +1,74 @@ +''' +Copyright 2019 Raytheon BBN Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +# Instructions for the the prototype APS2-TDM hardware. + +from .TdmInstructions import CustomInstruction, WriteAddr, Invalidate, LoadCmpVram +from .ControlFlow import LoadCmp, Call + +##custom APS OP_CODES +#APS_CLIFFORD_SET_SEED: Set 32 bit seed for random number generator +#APS_CLIFFORD_SET_OFFSET: Stores jump table starting offset +#APS_CLIFFORD_SET_SPACING: Stores jump table spacing (integer to shift left), +# including interal address representation +#APS_CLIFFORD_INVERSE_RESET: Reset inverse tracker +#APS_CLIFFORD_RAND: Choose randomly from set of 24 waveforms, +# returns starting address of jump table entry from RAM +#APS_CLIFFORD_INVERSE: Store waveform address of inverse waveform to be jumped to + +APS2_CUSTOM_DECODE = {"APS_RAND": 0, + "APS_CLIFFORD_RAND": 2, + "APS_CLIFFORD_INVERSE": 6, + "APS_CLIFFORD_INVERSE_RESET": 7, + "APS_CLIFFORD_SET_SEED": 3, + "APS_CLIFFORD_SET_OFFSET": 4, + "APS_CLIFFORD_SET_SPACING": 5} + +##Note that the expected call pattern for the randomizer is: + +# APS_CLIFFORD_RAND / APS_CLIFFORD_INVERSE +# LOADCMP +# CALL + +def RandomCliffordSetOffset(addr, offset): + return [Invalidate(addr, 0, tdm=False), + WriteAddr(adrr, offset, tdm=False), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0x3)] + +def RandomCliffordSetSpacing(addr, spacing): + return [Invalidate(addr, 0, tdm=False), + WriteAddr(adrr, offset, tdm=False), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0x3)] + +def RandomClifford(target, addr): + return [Invalidate(addr, 0, tdm=False), + CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + Call(target, load_addr=True)] + +def RandomCliffordInverse(target, addr): + return [Invalidate(addr, 0, tdm=False), + CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + Call(target, load_addr=True)] + +def RandomCliffordInverseReset(addr): #for now don't use addr + return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0x0006) + +def RandomCliffordSeed(seed): + raise NotImplementedError("APS_CLIFFORD_SET_SEED not implemented!") diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py new file mode 100644 index 00000000..6273e063 --- /dev/null +++ b/QGL/RandomCliffordTools.py @@ -0,0 +1,51 @@ +''' +Functions for dealing with random clifford pulse sequences + +Copyright 2019 Raytheon BBN Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +from . import config +from . import PatternUtils +from .PatternUtils import flatten, has_gate +from . import Channels +from . import ChannelLibraries +from . import PulseShapes +from . import PulsePrimitives +from .PulsePrimitives import Id, clear_pulse_cache +from .PulseSequencer import Pulse, PulseBlock, CompositePulse +from . import ControlFlow +from . import BlockLabel +from . import TdmInstructions # only for APS2-TDM +from . import APS2CustomInstructions + +def generate_clifford_jump_table(cliff_wires, jt_label = None): + """Generate the jump table that will be used to call into the clifford set""" + + if jt_label is None: + jt_label = BlockLabel.BlockLabel("JT") + if not isinstance(jt_label, BlockLabel.BlockLabel): + raise ValueError("Jump table label must be a BlockLabel.") + + cliff_labels = [f"C{n}" for n in range(24)] + jump_table = [jt_label] + for cl in cliff_labels: + jump_table.append(ControlFlow.Call(cl)) + jump_table.append(ControlFlow.Return()) + + for cliff, cl in zip(cliff_wires, cliff_labels): + cliff.insert(0, BlockLabel.BlockLabel(cl)) + cliff.append(ControlFlow.Return()) + + cliff_wires.insert(0, jump_table) From 5243c53620341101774e313c43460453244f0c27 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:40:30 -0500 Subject: [PATCH 120/235] Add string representation for custom instructions --- QGL/TdmInstructions.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index 38fa7cc7..a45fd3cd 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -36,6 +36,13 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + def __str__(self): + return f"{self.instruction}({hex(self.in_addr)}, {hex(self.out_addr)})" + + def __repr__(self): + return self.__str__() + + def MajorityVote(in_addr, out_addr, nmeas): # alternatively, append the loadcmpvram instruction when compiling (see STOREMEAS) return [LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 2**nmeas-1, True), CustomInstruction('MAJORITY', in_addr, out_addr)] @@ -74,6 +81,12 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + def __str__(self): + return f"{self.instruction}({hex(self.addr)}, {hex(self.value)})" + + def __repr__(self): + return self.__str__() + def WriteAddr(addr, value, channel=None, tdm=True): return WriteAddrInstruction('WRITEADDR', channel, 0, addr, value, tdm) @@ -108,6 +121,12 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + def __str__(self): + return f"{self.instruction}({hex(self.addr)}, {hex(self.mask)})" + + def __repr__(self): + return self.__str__() + def LoadCmpVram(addr, mask, tdm=True): return LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, mask, tdm) From 8b790b4f7a5fa97d208278396c172e5a576ad590 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:41:16 -0500 Subject: [PATCH 121/235] Set write flag to CALL to indicate load from VRAM --- QGL/ControlFlow.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index f733cb6a..5894d2f1 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -154,6 +154,13 @@ def __init__(self, target, load_addr=False): super(Call, self).__init__("CALL", target=target) self.load_addr = load_addr + def __str__(self): + if self.load_addr: + return f"{self.instruction}({str(self.target)})" + else: + return f"{self.instruction}({str(self.target)}, LOAD)" + + class Return(ControlInstruction): def __init__(self): From 96857168ec6f9a16755d9c6227b30eb181c57715 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:41:42 -0500 Subject: [PATCH 122/235] Update APS2 custom instructions for randomizer --- QGL/APS2CustomInstructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index cb082dc0..285f8685 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -63,12 +63,12 @@ def RandomClifford(target, addr): def RandomCliffordInverse(target, addr): return [Invalidate(addr, 0, tdm=False), - CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), + CustomInstruction("APS_CLIFFORD_INVERSE", 0x0, addr), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), Call(target, load_addr=True)] def RandomCliffordInverseReset(addr): #for now don't use addr - return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0x0006) + return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) def RandomCliffordSeed(seed): raise NotImplementedError("APS_CLIFFORD_SET_SEED not implemented!") From 1660fb3eba344acbf406808e4b328bf8b26b10c3 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:42:21 -0500 Subject: [PATCH 123/235] Tools for random clifford sequences. --- QGL/RandomCliffordTools.py | 46 +++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 6273e063..d7584c34 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -17,18 +17,23 @@ ''' from . import config +from functools import reduce from . import PatternUtils from .PatternUtils import flatten, has_gate from . import Channels from . import ChannelLibraries from . import PulseShapes from . import PulsePrimitives +from . import Compiler from .PulsePrimitives import Id, clear_pulse_cache from .PulseSequencer import Pulse, PulseBlock, CompositePulse from . import ControlFlow from . import BlockLabel from . import TdmInstructions # only for APS2-TDM -from . import APS2CustomInstructions +from .APS2CustomInstructions import * +from .Cliffords import C1, inverse_clifford, clifford_multiply + +VALID_CLIFFORD_TYPES = ('RandomAC',) def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" @@ -49,3 +54,42 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): cliff.append(ControlFlow.Return()) cliff_wires.insert(0, jump_table) + +def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, + inv_addr=0x3, inv_values=[]): + + if jt_label is None: + jt_label = BlockLabel.BlockLabel("JT") + if not isinstance(jt_label, BlockLabel.BlockLabel): + raise ValueError("Jump table label must be a BlockLabel.") + + for idx, seq in enumerate(seqs[:]): + if not PatternUtils.contains_runtime_pulses(seq): + continue + new_seq = [] + for pulse in seq: + if isinstance(pulse, Compiler.Waveform) and pulse.isRunTime \ + and pulse.label in VALID_CLIFFORD_TYPES: + has_random_cliff = True + new_seq.extend(RandomClifford(jt_label, cliff_addr)) + print("Inserting clifford pulse!") + else: + new_seq.append(pulse) + + if add_inv: + #insert reset after first wait + w_idx = next(i for i, v in enumerate(new_seq) if isinstance(v, ControlFlow.Wait)) + new_seq.insert(w_idx+1, RandomCliffordInverseReset(0x0)) + #insert at end of sequence or before last GOTO + if isinstance(new_seq[-1], ControlFlow.Goto): + new_seq[-1:-1] = RandomCliffordInverse(jt_label, inv_addr) + else: + new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) + + seqs[idx] = new_seq + +def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): + + c_seqs = [[clifford_type(qubit) for _ in seq] for seq in seqs] + inverses = [inverse_clifford(C1[reduce(clifford_multiply, seq)]) for seq in seqs] + return c_seqs, inverses From 107e25a72e89eb6a3530eb62296f69f31c73bfd1 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 25 Feb 2019 17:35:52 -0500 Subject: [PATCH 124/235] Even more WIP --- QGL/APS2CustomInstructions.py | 4 ++- QGL/Compiler.py | 40 ++++++++++++++--------- QGL/PulsePrimitives.py | 2 +- QGL/RandomCliffordTools.py | 4 +-- QGL/TdmInstructions.py | 7 ++-- QGL/drivers/APS2Pattern.py | 61 ++++++++++++++++++++++------------- 6 files changed, 74 insertions(+), 44 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 285f8685..6eb22054 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -29,7 +29,7 @@ # returns starting address of jump table entry from RAM #APS_CLIFFORD_INVERSE: Store waveform address of inverse waveform to be jumped to -APS2_CUSTOM_DECODE = {"APS_RAND": 0, +APS2_CUSTOM = {"APS_RAND": 0, "APS_CLIFFORD_RAND": 2, "APS_CLIFFORD_INVERSE": 6, "APS_CLIFFORD_INVERSE_RESET": 7, @@ -37,6 +37,8 @@ "APS_CLIFFORD_SET_OFFSET": 4, "APS_CLIFFORD_SET_SPACING": 5} +APS2_CUSTOM_DECODE = {v: k for k, v in APS2_CUSTOM.items()} + ##Note that the expected call pattern for the randomizer is: # APS_CLIFFORD_RAND / APS_CLIFFORD_INVERSE diff --git a/QGL/Compiler.py b/QGL/Compiler.py index bdf3ab2e..ce4992ab 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -36,6 +36,8 @@ from . import ControlFlow from . import BlockLabel from . import TdmInstructions # only for APS2-TDM +from . import APS2CustomInstructions +from . import RandomCliffordTools import gc logger = logging.getLogger(__name__) @@ -327,7 +329,7 @@ def setup_awg_channels(physicalChannels): return data -def bundle_wires(physWires, wfs, cliff_wires=None): +def bundle_wires(physWires, wfs): awgData = setup_awg_channels(physWires.keys()) for chan in physWires.keys(): _, awgChan = chan.label.rsplit('-', 1) @@ -335,8 +337,6 @@ def bundle_wires(physWires, wfs, cliff_wires=None): awgChan = 'ch' + awgChan awgData[chan.instrument][awgChan]['linkList'] = physWires[chan] awgData[chan.instrument][awgChan]['wfLib'] = wfs[chan] - if cliff_wires is not None and chan in cliff_wires.keys(): - awgData[chan.instrument][awgChan]['clifford_set'] = cliff_wires[chan] if hasattr(chan, 'correctionT'): awgData[chan.instrument][awgChan]['correctionT'] = chan.correctionT return awgData @@ -357,7 +357,7 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): +def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords=False): ChannelLibraries.channelLib.update_channelDict() clear_pulse_cache() @@ -394,7 +394,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): # Compile all the pulses/pulseblocks to sequences of pulses and control flow logger.info("Compiling sequences.") - wireSeqs = compile_sequences(seqs, channels) + wireSeqs = compile_sequences(seqs, channels, random_cliffords=random_cliffords) if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") @@ -459,11 +459,8 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): wfs = generate_waveforms(physWires) # If there are run-time pulses add these to the library - if PatternUtils.contains_runtime_pulses(seqs): + if random_cliffords: wfs = add_runtime_pulse_set(physWires, wfs) - cliff_wires = create_clifford_waveforms(physWires) - else: - cliff_wires = None # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") @@ -472,7 +469,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): # bundle wires on instruments, or channels depending # on whether we have one sequence per channel logger.info("Bundling wires.") - awgData = bundle_wires(physWires, wfs, cliff_wires=cliff_wires) + awgData = bundle_wires(physWires, wfs) return awgData def compile_to_hardware(seqs, @@ -589,7 +586,7 @@ def compile_to_hardware(seqs, return metafilepath -def compile_sequences(seqs, channels=set()): +def compile_sequences(seqs, channels=set(), random_cliffords=False): ''' Main function to convert sequences to miniLL's and waveform libraries. ''' @@ -603,6 +600,16 @@ def compile_sequences(seqs, channels=set()): subroutines = collect_specializations(seqs) seqs += subroutines + if random_cliffords: + qubits = [q for q in list(channels) if isinstance(q, Channels.Qubit)] + if len(qubits) > 1: + raise Exception("Too many qubits! Don't know how to deal with this yet.") + #replace cliffords with + cliffords = [[get_clifford_type(seqs)(n)] for n in range(24)] + RandomCliffordTools.generate_clifford_jump_table(cliffords) + RandomCliffordTools.insert_clifford_calls(seqs) + seqs += cliffords + #expand the channel definitions for anything defined in subroutines for func in subroutines: channels |= find_unique_channels(subroutines) @@ -664,10 +671,10 @@ def flatten_to_pulses(obj): wires[chan] += [copy(block)] continue # control flow broadcasts to all channels if channel attribute is None - if (isinstance(block, ControlFlow.ControlInstruction) or - isinstance(block, TdmInstructions.WriteAddrInstruction) or - isinstance(block, TdmInstructions.CustomInstruction) or - isinstance(block, TdmInstructions.LoadCmpVramInstruction)): + if isinstance(block, ControlFlow.ControlInstruction): #or + #isinstance(block, TdmInstructions.WriteAddrInstruction) or + #isinstance(block, TdmInstructions.CustomInstruction) or + #isinstance(block, TdmInstructions.LoadCmpVramInstruction)): # Need to deal with attaching measurements and edges to control # instruction. Until we have a proper solution for that, we will # always broadcast control instructions to all channels @@ -676,6 +683,9 @@ def flatten_to_pulses(obj): for chan in block_channels: wires[chan] += [copy(block)] continue + #Don't propagate VRAM instructions + if isinstance(block, TdmInstructions.VRAMInstruction) or isinstance(block, TdmInstructions.CustomInstruction): + continue # propagate frame change from nodes to edges for chan in channels: if block.pulses[chan].frameChange == 0: diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 596c79d2..a648e044 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -372,7 +372,7 @@ def AC(qubit, cliffNum): #Now a big else if chain for to get the specific Clifford if cliffNum == 0: #Identity gate - return Id(qubit, length=0) + return Id(qubit)#Id(qubit, length=0) elif cliffNum == 1: #X90 return X90(qubit) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index d7584c34..d4a0d26e 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -68,11 +68,11 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, continue new_seq = [] for pulse in seq: - if isinstance(pulse, Compiler.Waveform) and pulse.isRunTime \ + if isinstance(pulse, Pulse) and pulse.isRunTime \ and pulse.label in VALID_CLIFFORD_TYPES: has_random_cliff = True new_seq.extend(RandomClifford(jt_label, cliff_addr)) - print("Inserting clifford pulse!") + #print("Inserting clifford pulse!") else: new_seq.append(pulse) diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index a45fd3cd..6dfdd7d1 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -58,7 +58,10 @@ def DecodeSetRounds(in_addr, out_addr, value): # TODO: the rest of the CUSTOM instructions -class WriteAddrInstruction(object): +class VRAMInstruction(object): + pass + +class WriteAddrInstruction(VRAMInstruction): def __init__(self, name, channel, modifier, addr, value, tdm, **kwargs): self.instruction = name @@ -99,7 +102,7 @@ def CrossBar(addr, value, channel=None, tdm=True): # should this be a high-level def StoreMeas(addr, value, channel=None, tdm=True): return WriteAddrInstruction('STOREMEAS', channel, 5, addr, value, tdm) -class LoadCmpVramInstruction(object): +class LoadCmpVramInstruction(VRAMInstruction): def __init__(self, name, use_vram, addr, mask, tdm): # TODO: sanity checks on input values diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 5e6db0d7..42c34f06 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -32,7 +32,7 @@ from QGL.PatternUtils import hash_pulse, flatten from QGL import TdmInstructions from QGL import APS2CustomInstructions -from QGL.APS2CustomInstructions import APS2_CUSTOM_DECODE +from QGL.APS2CustomInstructions import APS2_CUSTOM, APS2_CUSTOM_DECODE # Python 2/3 compatibility: use 'int' that subclasses 'long' from builtins import int @@ -306,7 +306,10 @@ def __str__(self): store_addr = self.payload & 0xFFFF load_addr = (self.payload >> 16) & 0xFFFF instruction = (self.payload >> 32) & 0xFF - instructionAPS = TDM_CUSTOM_DECODE[instruction] + if self.decode_as_tdm: + instructionAPS = TDM_CUSTOM_DECODE[instruction] + else: + instructionAPS = APS2_CUSTOM_DECODE[instruction] out += " | instruction={0} ({1}), load_addr=0x{2:0x}, store_addr=0x{3:0x}".format(instruction, instructionAPS, load_addr, store_addr) elif instrOpCode == WRITEADDR: @@ -510,12 +513,11 @@ def LoadCmpVram(addr, mask, label=None): payload = (1 << 48) | (mask << 16) | addr return Instruction(header, payload, label=label) - -def preprocess(seqs, shapeLib, clifford_set=False): +def preprocess(seqs, shapeLib): seqs = PatternUtils.convert_lengths_to_samples( seqs, SAMPLING_RATE, ADDRESS_UNIT, Compiler.Waveform) wfLib = build_waveforms(seqs, shapeLib) - inject_modulation_cmds(seqs, force_phase_update=clifford_set) + inject_modulation_cmds(seqs) return seqs, wfLib @@ -715,7 +717,7 @@ def synchronize_clocks(seqs): syncInstructions = [list(filter( lambda s: isinstance(s, ControlFlow.ControlInstruction), seq)) for seq in seqs if seq] - + #import pdb; pdb.set_trace() # Add length to control-flow instructions to make accumulated time match at end of CFI. # Keep running tally of how much each channel has been shifted so far. localShift = [0 for _ in syncInstructions] @@ -801,7 +803,7 @@ def create_seq_instructions(seqs, offsets, label = None): isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction) or - isinstance(entry, TdmInstructions.Instruction) or + #isinstance(entry, TdmInstructions.Instruction) or ?????? isinstance(entry, TdmInstructions.LoadCmpVramInstruction)): if isinstance(entry, BlockLabel.BlockLabel): # carry label forward to next entry @@ -835,11 +837,15 @@ def create_seq_instructions(seqs, offsets, label = None): instructions.append(LoadCmpVram(entry.addr, entry.mask, label=label)) # some TDM instructions are ignored by the APS elif isinstance(entry, TdmInstructions.CustomInstruction): - pass + try: + instructions.append(Custom(entry.in_addr, entry.out_addr, + APS2_CUSTOM[entry.instruction], label=label)) + except KeyError as e: + raise Exception(f"Got unknown APS2 instruction: {e.args[0]} in {str(entry)}.") elif isinstance(entry, TdmInstructions.WriteAddrInstruction): if entry.instruction == 'INVALIDATE' and entry.tdm == False: instructions.append(Invalidate(entry.addr, entry.value, label=label)) - + label=None #Reset label to prevent it propagating in a jump table continue if seq_idx == 0: @@ -983,20 +989,29 @@ def create_instr_data(seqs, offsets, cache_lines): def resolve_symbols(seq): - symbols = {} - # create symbol look-up table - for ct, entry in enumerate(seq): - if entry.label and entry.label not in symbols: - symbols[entry.label] = ct - # then update - for (ct, entry) in enumerate(seq): - if entry.target: - # find next available label. The TDM may miss some labels if branches only contain waveforms (which are ignored) - for k in range(len(seq)-ct): - temp = seq[ct+k] - if temp.target in symbols: - break - entry.address = symbols[temp.target] + + labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] + import pdb; pdb.set_trace() + symbols = {label: idx for idx, label in labeled_entries} + print(f"Found labels: {symbols}") + for entry in seq: + if entry.target is not None and entry.target in symbols.keys(): + entry.address = symbols[entry.target] + + # symbols = {} + # # create symbol look-up table + # for ct, entry in enumerate(seq): + # if entry.label and entry.label not in symbols: + # symbols[entry.label] = ct + # # then update + # for (ct, entry) in enumerate(seq): + # if entry.target: + # # find next available label. The TDM may miss some labels if branches only contain waveforms (which are ignored) + # for k in range(len(seq)-ct): + # temp = seq[ct+k] + # if temp.label in symbols: + # break + # entry.address = symbols[temp.label] def compress_marker(markerLL): From c36c771e1738135d05047eef19b7647986bffe9c Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 25 Feb 2019 17:56:00 -0500 Subject: [PATCH 125/235] Remove debug. --- QGL/drivers/APS2Pattern.py | 1 - 1 file changed, 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 42c34f06..34c97797 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -991,7 +991,6 @@ def create_instr_data(seqs, offsets, cache_lines): def resolve_symbols(seq): labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] - import pdb; pdb.set_trace() symbols = {label: idx for idx, label in labeled_entries} print(f"Found labels: {symbols}") for entry in seq: From b859abec0798d7619ad666ddff1e247bc1cd09e2 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 25 Feb 2019 21:33:07 -0500 Subject: [PATCH 126/235] Make sure Custom instructions not dropped by pattern generator! --- QGL/APS2CustomInstructions.py | 4 ++-- QGL/Compiler.py | 22 ++++++++++++++-------- QGL/PulseSequencer.py | 4 ++++ QGL/RandomCliffordTools.py | 14 +++++++++++++- QGL/drivers/APS2Pattern.py | 6 +++--- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 6eb22054..3b269884 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -47,13 +47,13 @@ def RandomCliffordSetOffset(addr, offset): return [Invalidate(addr, 0, tdm=False), - WriteAddr(adrr, offset, tdm=False), + WriteAddr(addr, offset, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0x3)] def RandomCliffordSetSpacing(addr, spacing): return [Invalidate(addr, 0, tdm=False), - WriteAddr(adrr, offset, tdm=False), + WriteAddr(addr, spacing, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0x3)] diff --git a/QGL/Compiler.py b/QGL/Compiler.py index ce4992ab..57abd552 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -236,7 +236,15 @@ def generate_waveforms(physicalWires): return wfs def get_clifford_type(wire, allowed_types=("RandomAC")): - rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] + rt_pulses = [] + for pulse in flatten(wire): + if isinstance(pulse, Pulse) and pulse.isRunTime: + rt_pulses.append(pulse) + elif isinstance(pulse, PulseBlock) and pulse.isRunTime: + for p in pulse.pulses.values(): + if p.isRunTime: + rt_pulses.append(p) + if len(rt_pulses) == 0: return None pulse_type = list(set([pulse.label for pulse in rt_pulses])) @@ -399,7 +407,6 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") return - logger.debug('') logger.debug("Now after gating constraints:") # apply gating constraints @@ -671,10 +678,9 @@ def flatten_to_pulses(obj): wires[chan] += [copy(block)] continue # control flow broadcasts to all channels if channel attribute is None - if isinstance(block, ControlFlow.ControlInstruction): #or - #isinstance(block, TdmInstructions.WriteAddrInstruction) or - #isinstance(block, TdmInstructions.CustomInstruction) or - #isinstance(block, TdmInstructions.LoadCmpVramInstruction)): + if (isinstance(block, ControlFlow.ControlInstruction) or + isinstance(block, TdmInstructions.VRAMInstruction) or + isinstance(block, TdmInstructions.CustomInstruction)): # Need to deal with attaching measurements and edges to control # instruction. Until we have a proper solution for that, we will # always broadcast control instructions to all channels @@ -684,8 +690,8 @@ def flatten_to_pulses(obj): wires[chan] += [copy(block)] continue #Don't propagate VRAM instructions - if isinstance(block, TdmInstructions.VRAMInstruction) or isinstance(block, TdmInstructions.CustomInstruction): - continue + #if isinstance(block, TdmInstructions.VRAMInstruction) or isinstance(block, TdmInstructions.CustomInstruction): + # continue # propagate frame change from nodes to edges for chan in channels: if block.pulses[chan].frameChange == 0: diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 463b273b..c29f43f8 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -198,6 +198,10 @@ def frameChange(self): def isZero(self): return all(p.isZero for p in self.pulses) + @property + def isRunTime(self): + return any(hasattr(entry, 'isRunTime') and entry.isRunTime for entry in self.pulses) + class PulseBlock(object): ''' diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index d4a0d26e..6863f0b4 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -66,16 +66,27 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, for idx, seq in enumerate(seqs[:]): if not PatternUtils.contains_runtime_pulses(seq): continue + new_seq = [] + for pulse in seq: if isinstance(pulse, Pulse) and pulse.isRunTime \ and pulse.label in VALID_CLIFFORD_TYPES: has_random_cliff = True new_seq.extend(RandomClifford(jt_label, cliff_addr)) - #print("Inserting clifford pulse!") + print("Inserting clifford pulse!") else: new_seq.append(pulse) + info_seqs = [] + info_seqs.extend(RandomCliffordSetOffset(0x1, 0x0)) + info_seqs.extend(RandomCliffordSetSpacing(0x2, 0x1)) + + if isinstance(seq[0], BlockLabel.BlockLabel): + new_seq[1:1] = info_seqs + else: + new_seq[0:0] = info_seqs + if add_inv: #insert reset after first wait w_idx = next(i for i, v in enumerate(new_seq) if isinstance(v, ControlFlow.Wait)) @@ -88,6 +99,7 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, seqs[idx] = new_seq + def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): c_seqs = [[clifford_type(qubit) for _ in seq] for seq in seqs] diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 34c97797..fef8ecd1 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -634,10 +634,10 @@ def inject_modulation_cmds(seqs, force_phase_update=False): #copies to avoid same object having different timestamps later #copy through BlockLabel - if isinstance(entry, BlockLabel.BlockLabel): + if isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction): mod_seq.append(copy(entry)) #mostly copy through control-flow - elif isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, TdmInstructions.LoadCmpVramInstruction) or isinstance(entry, TdmInstructions.WriteAddrInstruction): + elif isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, TdmInstructions.VRAMInstruction): #heuristic to insert phase reset before trigger if we have modulation commands if isinstance(entry, ControlFlow.Wait): if not ( no_modulation_cmds and (cur_freq == 0) and (cur_phase == 0)): @@ -797,7 +797,6 @@ def create_seq_instructions(seqs, offsets, label = None): write_flags = [True] * len(entries) for ct, (entry, seq_idx) in enumerate(entries): - #use first non empty sequence for control flow if seq_idx == first_non_empty and ( isinstance(entry, ControlFlow.ControlInstruction) or @@ -837,6 +836,7 @@ def create_seq_instructions(seqs, offsets, label = None): instructions.append(LoadCmpVram(entry.addr, entry.mask, label=label)) # some TDM instructions are ignored by the APS elif isinstance(entry, TdmInstructions.CustomInstruction): + print(str(entry)) try: instructions.append(Custom(entry.in_addr, entry.out_addr, APS2_CUSTOM[entry.instruction], label=label)) From 0d356558d03a49475f018065666be54171cd7565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Tue, 26 Feb 2019 09:44:03 -0500 Subject: [PATCH 127/235] Add DCsources and spec.analyzers to db With relations to corresponding sources (for paramp bias) and LO --- QGL/ChannelLibraries.py | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 0f4bde94..c7955213 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -142,7 +142,10 @@ def get_current_channels(self): self.channelDatabase.receivers + self.channelDatabase.transceivers + self.channelDatabase.instruments + - self.channelDatabase.processors) + self.channelDatabase.processors + + self.channelDatabase.attenuators + + self.channelDatabase.DCSources + + self.channelDatabase.spectrum_analyzers) def update_channelDict(self): self.channelDict = {c.label: c for c in self.get_current_channels()} @@ -315,11 +318,15 @@ def new_TDM(self, label, address): @check_for_duplicates def new_spectrum_analzyer(self, label, address, source): - return Channels.SpectrumAnalyzer(label=label, model="SpectrumAnalyzer", address=address, LO_source=source) + sa = Channels.SpectrumAnalyzer(label=label, model="SpectrumAnalyzer", address=address, LO_source=source, channel_db=self.channelDatabase) + self.add_and_update_dict(sa) + return sa @check_for_duplicates def new_DC_source(self, label, address): - return Channels.DCSource(label=label, model="YokogawaGS200", address=address, standalone=True) + dcsource = Channels.DCSource(label=label, model="YokogawaGS200", address=address, standalone=True, channel_db=self.channelDatabase) + self.add_and_update_dict(dcsource) + return dcsource @check_for_duplicates def new_APS2_rack(self, label, ip_addresses, tdm_ip=None): @@ -386,7 +393,7 @@ def new_qubit(self, label, **kwargs): return thing @check_for_duplicates - def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, reference=None): + def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, reference='10MHz'): thing = Channels.Generator(label=label, model=model, address=address, power=power, frequency=frequency, reference=reference, channel_db=self.channelDatabase) From 7380af300195f373e7b0082bd26362c8a8c82c53 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Tue, 26 Feb 2019 09:51:17 -0500 Subject: [PATCH 128/235] bqplot fixes -GR --- QGL.egg-info/PKG-INFO | 10 +++++++ QGL.egg-info/SOURCES.txt | 43 +++++++++++++++++++++++++++++++ QGL.egg-info/dependency_links.txt | 1 + QGL.egg-info/requires.txt | 6 +++++ QGL.egg-info/top_level.txt | 1 + QGL/PulseSequencePlotter.py | 7 +++-- 6 files changed, 64 insertions(+), 4 deletions(-) create mode 100644 QGL.egg-info/PKG-INFO create mode 100644 QGL.egg-info/SOURCES.txt create mode 100644 QGL.egg-info/dependency_links.txt create mode 100644 QGL.egg-info/requires.txt create mode 100644 QGL.egg-info/top_level.txt diff --git a/QGL.egg-info/PKG-INFO b/QGL.egg-info/PKG-INFO new file mode 100644 index 00000000..ac9daf85 --- /dev/null +++ b/QGL.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: QGL +Version: 2.1 +Summary: UNKNOWN +Home-page: https://github.com/BBN-Q/QGL +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/QGL.egg-info/SOURCES.txt b/QGL.egg-info/SOURCES.txt new file mode 100644 index 00000000..79998f6f --- /dev/null +++ b/QGL.egg-info/SOURCES.txt @@ -0,0 +1,43 @@ +README.md +setup.py +QGL/BlockLabel.py +QGL/ChannelLibraries.py +QGL/Channels.py +QGL/Cliffords.py +QGL/Compiler.py +QGL/ControlFlow.py +QGL/GSTTools.py +QGL/PatternUtils.py +QGL/Plotting.py +QGL/PulsePrimitives.py +QGL/PulseSequencePlotter.py +QGL/PulseSequencer.py +QGL/PulseShapes.py +QGL/Scheduler.py +QGL/TdmInstructions.py +QGL/Tomography.py +QGL/__init__.py +QGL/config.py +QGL/config_location.py +QGL/mm.py +QGL.egg-info/PKG-INFO +QGL.egg-info/SOURCES.txt +QGL.egg-info/dependency_links.txt +QGL.egg-info/requires.txt +QGL.egg-info/top_level.txt +QGL/BasicSequences/AllXY.py +QGL/BasicSequences/BlankingSweeps.py +QGL/BasicSequences/CR.py +QGL/BasicSequences/Decoupling.py +QGL/BasicSequences/Feedback.py +QGL/BasicSequences/FlipFlop.py +QGL/BasicSequences/RB.py +QGL/BasicSequences/Rabi.py +QGL/BasicSequences/SPAM.py +QGL/BasicSequences/T1T2.py +QGL/BasicSequences/__init__.py +QGL/BasicSequences/helpers.py +QGL/drivers/APS2Pattern.py +QGL/drivers/APSPattern.py +QGL/drivers/TekPattern.py +QGL/drivers/__init__.py \ No newline at end of file diff --git a/QGL.egg-info/dependency_links.txt b/QGL.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/QGL.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/QGL.egg-info/requires.txt b/QGL.egg-info/requires.txt new file mode 100644 index 00000000..fc2fa0c8 --- /dev/null +++ b/QGL.egg-info/requires.txt @@ -0,0 +1,6 @@ +bbndb>=0.1 +numpy>=1.11.1 +scipy>=0.17.1 +networkx>=1.11 +h5py>=2.6.0 +bokeh>=0.12.13 diff --git a/QGL.egg-info/top_level.txt b/QGL.egg-info/top_level.txt new file mode 100644 index 00000000..0e533603 --- /dev/null +++ b/QGL.egg-info/top_level.txt @@ -0,0 +1 @@ +QGL diff --git a/QGL/PulseSequencePlotter.py b/QGL/PulseSequencePlotter.py index 5833a35b..27b61d87 100644 --- a/QGL/PulseSequencePlotter.py +++ b/QGL/PulseSequencePlotter.py @@ -25,8 +25,6 @@ import os.path import json from importlib import import_module -from ipywidgets import interact, VBox, IntSlider -import ipywidgets as widgets import numpy as np from . import config @@ -92,6 +90,7 @@ def plot_pulse_files(metafile, time=True, backend='bqplot'): if backend=='matplotlib': import matplotlib.pyplot as plt + from ipywidgets import interact, IntSlider def update_plot(seq_ind): for line_name in line_names: dat = data_dicts[f"{line_name}_{seq_ind}"] @@ -101,7 +100,7 @@ def update_plot(seq_ind): elif backend=='bqplot': from bqplot import DateScale, LinearScale, Axis, Lines, Figure, Tooltip from bqplot.colorschemes import CATEGORY10, CATEGORY20 - from ipywidgets import interact, IntSlider + from ipywidgets import interact, IntSlider, VBox sx = LinearScale() sy = LinearScale(min=-1.0, max=2*len(line_names)-1.0) if time: @@ -116,7 +115,7 @@ def update_plot(seq_ind): x_mult = 1.0e9 if time else 1 for i, line_name in enumerate(line_names): dat = data_dicts[f"{line_name}_1"] - lines.append(Lines(labels=[line_name], x=x_mult*dat['x'], y=dat['y'], scales={'x': sx, 'y': sy}, + lines.append(Lines(labels=[line_name], x=x_mult*dat['x'], y=dat['y'], scales={'x': sx, 'y': sy}, tooltip=tt, animate=False, colors=[colors[i]])) slider = IntSlider(min=1, max=num_seqs, step=1, description='Segment', value=1) From 923deb370852c5bca53d578a0fab243231fa5d87 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 10:50:59 -0500 Subject: [PATCH 129/235] Add color for custom instructions --- QGL/drivers/APS2Pattern.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index fef8ecd1..26a6e388 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1513,7 +1513,8 @@ def display_raw_file(filename): colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), - "MARKER": QColor(150,150,200)} + "MARKER": QColor(150,150,200), + "CUSTOM": QColor(200,65,200)} class App(QWidget): From 160aeb1d98d68c65de5110bc24ec18976ea0c1b0 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 11:10:54 -0500 Subject: [PATCH 130/235] Don't drop WRITEADDR instructions! --- QGL/drivers/APS2Pattern.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 26a6e388..feb1049b 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -803,7 +803,7 @@ def create_seq_instructions(seqs, offsets, label = None): isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction) or #isinstance(entry, TdmInstructions.Instruction) or ?????? - isinstance(entry, TdmInstructions.LoadCmpVramInstruction)): + isinstance(entry, TdmInstructions.VRAMInstruction)): if isinstance(entry, BlockLabel.BlockLabel): # carry label forward to next entry label = entry @@ -843,8 +843,11 @@ def create_seq_instructions(seqs, offsets, label = None): except KeyError as e: raise Exception(f"Got unknown APS2 instruction: {e.args[0]} in {str(entry)}.") elif isinstance(entry, TdmInstructions.WriteAddrInstruction): + print(str(entry)) if entry.instruction == 'INVALIDATE' and entry.tdm == False: instructions.append(Invalidate(entry.addr, entry.value, label=label)) + if entry.instruction == 'WRITEADDR' and entry.tdm == False: + instructions.append(WriteAddr(entry.addr, entry.value, label=label)) label=None #Reset label to prevent it propagating in a jump table continue @@ -1514,7 +1517,8 @@ def display_raw_file(filename): colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), "MARKER": QColor(150,150,200), - "CUSTOM": QColor(200,65,200)} + "CUSTOM": QColor(200,65,200), + "WRITEADDR": QColor(245, 105, 65)} class App(QWidget): From 3d736f6dfe6878928bc6a51fba7513216d86e279 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 11:58:32 -0500 Subject: [PATCH 131/235] Add sequence debugger. --- QGL/test_helpers.py | 0 tests/aps_debug_watcher.py | 219 +++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 QGL/test_helpers.py create mode 100644 tests/aps_debug_watcher.py diff --git a/QGL/test_helpers.py b/QGL/test_helpers.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/aps_debug_watcher.py b/tests/aps_debug_watcher.py new file mode 100644 index 00000000..4f389e8f --- /dev/null +++ b/tests/aps_debug_watcher.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python + +import sys +import socket +import struct +try: + from QGL.drivers import APS2Pattern + haveQGL = True +except: + haveQGL = False +import numpy as np +from ansicolor import * # from ansicolors + +ip = sys.argv[1] +port = 0xbb50 + +short_time = True + +raw_mode = '-r' in sys.argv +tdm_mode = '-tdm' in sys.argv + +print("Connecting to APS Debug Port at {0}:{1}".format(ip,port)) + +PACKET_SIZE=168//8+1 + +if short_time: + PACKET_SIZE -= 2 + +MODE_UNKNOWN = 0 +MODE_SEQUENCER = 1 +MODE_RAM = 2 +MODE_TRIGGER = 3 + +def bittest(v, b): + if ((v & (1< 0: + extraData = data[block_size:] + data = data[:block_size] + + for cnt in range(num_packets): + start = cnt * PACKET_SIZE + end = (cnt+1)*PACKET_SIZE + packet = data[start:end] + packet_bytes = bytearray(packet) + if raw_mode: + print(packet_bytes) + continue + + if packet[0] == 0x1: + mode = MODE_SEQUENCER + elif packet[0] in [0x2,0x3, 0x4, 0x5]: + mode = MODE_RAM + elif packet[0] == 0x6: + mode = MODE_TRIGGER + else: + mode = MODE_UNKNOWN + + + if short_time: + uptime_seconds = packet_bytes[1:3] + uptime_nanoseconds = packet_bytes[3:7] + + uptime_seconds = struct.unpack(">H", uptime_seconds)[0] + else: + uptime_seconds = packet_bytes[1:5] + uptime_nanoseconds = packet_bytes[5:9] + + uptime_seconds = struct.unpack(">I", uptime_seconds)[0] + + uptime_nanoseconds = struct.unpack(">I", uptime_nanoseconds)[0] + + #print(uptime_seconds, uptime_nanoseconds) + + uptime_nanoseconds = uptime_nanoseconds/1e9 + uptime = uptime_seconds + uptime_nanoseconds + + if short_time: + start = 8 + else: + start = 10 + + if mode == MODE_SEQUENCER: + + if short_time: + triggerWord = packet[7] + else: + triggerWord = packet[9] + + haltBits = packet[start] + + haltStr = formatHaltBits(haltBits) + + instructionAddr, start = get_bytes(packet, start, 4) + instructionAddr = bytearray(instructionAddr) + # clear halt bits, only the first two bits are part of the Addr + instructionAddr[0] = instructionAddr[0] & 0x3 + instructionAddr = struct.unpack(">I", instructionAddr)[0] + + seq_debug_data, start = get_bytes(packet, start, 8) + seq_debug_data = np.fromstring(seq_debug_data[::-1], dtype=np.uint64) + + if haveQGL: + instruction = APS2Pattern.Instruction.unflatten(seq_debug_data, decode_as_tdm = tdm_mode) + else: + instruction = '' + + h = "{:016x}".format(seq_debug_data[0]).upper() + + print(red("{:10}".format("Sequencer")), \ + white(": {:4}".format(instructionAddr)),\ + blue("0x{}".format(h)), \ + yellow(" {:6.8f}".format(uptime)), \ + haltStr, \ + "T:0x{:x}".format(triggerWord), \ + " {} ".format(instruction)) + # if instructionAddr > 100: + # sys.exit() + elif mode == MODE_RAM: + + zeros, start = get_bytes(packet, start, 4) + vram_addr, start = get_bytes(packet, start, 4) + vram_data, start = get_bytes(packet, start, 4) + + vram_header = struct.unpack(">I", zeros)[0] + vram_addr = struct.unpack(">I", vram_addr)[0] + vram_data = struct.unpack(">I", vram_data)[0] + + if packet[0] == 0x2: + ram_mode = "Valid" + + if packet[0] == 0x3: + ram_mode = "Write" + + if packet[0] == 0x4: + ram_mode = "Send" + + if packet[0] == 0x5: + ram_mode = "Recv" + + print(green("{:10}".format("RAM")), \ + white(": {:>23}".format(ram_mode)),\ + yellow(" {:6.8f}".format(uptime)), \ + "addr = 0x{:08X} data = 0x{:08X}".format(vram_addr, vram_data)) + + elif mode == MODE_TRIGGER: + + zeros, start = get_bytes(packet, start, 8) + syncBits, start = get_bytes(packet,start,1) + haltBits, start = get_bytes(packet, start, 1) + triggers, start = get_bytes(packet, start, 1) + triggerWord, start = get_bytes(packet,start,1) + + for z in zeros: + if z != 0: + print("Error expected 0 not", z) + + haltBits = haltBits[0] + syncBits = syncBits[0] + triggers = triggers[0] + triggerWord = triggerWord[0] + + haltStr = formatHaltBits(haltBits) + + syncWF = (syncBits >> 3) & 0xF + syncMarker = (syncBits >> 1) & 0x3 + syncMod = syncBits & 0x1 + + + halt = bittest(syncBits,4) + pc_jump = bittest(syncBits, 5) + tready = bittest(syncBits, 6) + tvalid = bittest(syncBits, 7) + + syncStr = "TV: {} TR: {} PJ: {} H: {} SWF: 0x{} SMa: 0x{} SMo: 0x{}".format(tvalid, tready, pc_jump, halt, syncWF, syncMarker, syncMod) + + trigger = bittest(triggers,1) + triggerWordValid = bittest(triggers,0) + + triggerStr = "t = {} tWV = {} tW = 0x{:0X}".format(trigger,triggerWordValid, triggerWord) + + print(green("{:10}".format("Trigger")), \ + white(": {:>23}".format('')),\ + yellow(" {:6.8f}".format(uptime)), \ + haltStr, \ + syncStr, \ + triggerStr) + elif mode == MODE_UNKNOWN: + print(green("{:10}".format("Unknown")),packet_bytes) + +s.close() From b7a15f741e3d1f6cd3ac44eca689fdd9dac55295 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:31:14 -0500 Subject: [PATCH 132/235] Properly set indirect mode on CALL --- QGL/APS2CustomInstructions.py | 4 ++-- QGL/ControlFlow.py | 6 +++--- QGL/drivers/APS2Pattern.py | 25 ++++++++++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 3b269884..1d93cf38 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -61,13 +61,13 @@ def RandomClifford(target, addr): return [Invalidate(addr, 0, tdm=False), CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - Call(target, load_addr=True)] + Call(target, indirect=True)] def RandomCliffordInverse(target, addr): return [Invalidate(addr, 0, tdm=False), CustomInstruction("APS_CLIFFORD_INVERSE", 0x0, addr), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - Call(target, load_addr=True)] + Call(target, indirect=True)] def RandomCliffordInverseReset(addr): #for now don't use addr return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index 5894d2f1..cc1e6327 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -150,15 +150,15 @@ def __init__(self, target): class Call(ControlInstruction): # target is a BlockLabel - def __init__(self, target, load_addr=False): + def __init__(self, target, indirect=False): super(Call, self).__init__("CALL", target=target) - self.load_addr = load_addr + self.indirect = indirect def __str__(self): if self.load_addr: return f"{self.instruction}({str(self.target)})" else: - return f"{self.instruction}({str(self.target)}, LOAD)" + return f"{self.instruction}({str(self.target)}, INDIRECT)" diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index feb1049b..eb9ccfe4 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -341,12 +341,11 @@ def __str__(self): elif instrOpCode == LOADCMP: addr = self.payload & 0xFFFF mask = (self.payload >> 16) & 0xFFFF - use_ram = (self.payload >> 48) & 0x1 - if self.decode_as_tdm and not use_ram: + if self.decode_as_tdm and not self.use_ram: out += "WAITMEAS" else: src = "EXT" - if use_ram: + if self.use_ram: src = "RAM" out += "LOADCMP | source={0}, addr=0x{1:0x}, read_mask=0x{2:0x}".format(src, addr, mask) return out @@ -374,7 +373,17 @@ def writeFlag(self): @writeFlag.setter def writeFlag(self, value): - self.header |= value & 0x1 + self.header &= ~(0x1 << 0) #clear bit + self.header |= (value & 0x1) #set bit + + @property + def use_ram(self): + return ((self.payload >> 48) & 0x1) + + @use_ram.setter + def use_ram(self, value): + self.payload &= ~(0x1 << 48) #clear bit + self.payload |= ((value & 0x1) << 48) @property def opcode(self): @@ -447,8 +456,10 @@ def Goto(addr, label=None): return Command(GOTO, addr, label=label) -def Call(addr, load_addr=False, label=None): - return Command(CALL, addr, label=label, write=load_addr) +def Call(addr, indirect=False, label=None): + cmd = Command(CALL, addr, label=label) + cmd.use_ram = indirect + return cmd def Return(label=None): @@ -821,7 +832,7 @@ def create_seq_instructions(seqs, offsets, label = None): elif isinstance(entry, ControlFlow.Goto): instructions.append(Goto(entry.target, label=label)) elif isinstance(entry, ControlFlow.Call): - instructions.append(Call(entry.target, load_addr = entry.load_addr, label=label)) + instructions.append(Call(entry.target, indirect = entry.indirect, label=label)) elif isinstance(entry, ControlFlow.Repeat): instructions.append(Repeat(entry.target, label=label)) # value argument commands From 5d641c0ae2e810636b3e202ab4c75d06a770df5a Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:55:27 -0500 Subject: [PATCH 133/235] Fix block labels on Calls. --- QGL/RandomCliffordTools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 6863f0b4..d4fc97bb 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -43,14 +43,14 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): if not isinstance(jt_label, BlockLabel.BlockLabel): raise ValueError("Jump table label must be a BlockLabel.") - cliff_labels = [f"C{n}" for n in range(24)] + cliff_labels = [BlockLabel.BlockLabel(f"C{n}") for n in range(24)] jump_table = [jt_label] for cl in cliff_labels: jump_table.append(ControlFlow.Call(cl)) jump_table.append(ControlFlow.Return()) for cliff, cl in zip(cliff_wires, cliff_labels): - cliff.insert(0, BlockLabel.BlockLabel(cl)) + cliff.insert(0, cl) cliff.append(ControlFlow.Return()) cliff_wires.insert(0, jump_table) From f02bdb296751fb7b6f5abb85c5b694cbdfd88b76 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:55:40 -0500 Subject: [PATCH 134/235] Make more pretty --- QGL/drivers/APS2Pattern.py | 23 ++++++++++++++++++++--- 1 file changed, 20 insertions(+), 3 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index eb9ccfe4..e88e8d79 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1523,16 +1523,25 @@ def display_raw_file(filename): if len(sys.argv) == 2: from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView - from PyQt5.QtGui import QIcon, QColor + from PyQt5.QtGui import QIcon, QColor, QFont + + table_font = QFont("Arial", weight=57) colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), "MARKER": QColor(150,150,200), "CUSTOM": QColor(200,65,200), - "WRITEADDR": QColor(245, 105, 65)} + "WRITEADDR": QColor(245, 105, 65), + "INVALIDATE": QColor(245, 105, 65), + "CALL": QColor(65, 205, 245), + "RET": QColor(65, 205, 245), + "LOADCMP": QColor(245, 225, 65), + "MODULATION": QColor(175, 255, 185)} class App(QWidget): + COLUMN_COUNT = 7 + def __init__(self, instructions): super().__init__() self.title = 'APS2 Disassembled Instructions' @@ -1559,7 +1568,7 @@ def createTable(self): # Create table self.tableWidget = QTableWidget() self.tableWidget.setRowCount(len(self.instructions)) - self.tableWidget.setColumnCount(7) + self.tableWidget.setColumnCount(self.COLUMN_COUNT) for k, instr in enumerate(self.instructions): fields = str(instr).replace(',','').replace(';', '').split(" ") @@ -1572,9 +1581,17 @@ def createTable(self): for l, f in enumerate(fields): text = fields[l] item = QTableWidgetItem(text) + item.setFont(table_font) if color: item.setBackground(color) self.tableWidget.setItem(k,l, item) + if l < self.COLUMN_COUNT-1: + for j in range(l+1, self.COLUMN_COUNT): + item = QTableWidgetItem("") + if color: + item.setBackground(color) + self.tableWidget.setItem(k, j, item) + self.tableWidget.move(0,0) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) From b9a2ff0f754a48a686812c27dbd4d9ff28ccca53 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:59:42 -0500 Subject: [PATCH 135/235] Bold font --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index e88e8d79..12bbfddf 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1525,7 +1525,7 @@ def display_raw_file(filename): from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView from PyQt5.QtGui import QIcon, QColor, QFont - table_font = QFont("Arial", weight=57) + table_font = QFont("Arial", weight=QFont.Bold) colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), From bae3736fa9bb3e83c94359f7a0f68597d7459378 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 26 Feb 2019 15:03:23 -0500 Subject: [PATCH 136/235] Get rid of accidental egg-info files --- QGL.egg-info/PKG-INFO | 10 ------- QGL.egg-info/SOURCES.txt | 43 ------------------------------- QGL.egg-info/dependency_links.txt | 1 - QGL.egg-info/requires.txt | 6 ----- QGL.egg-info/top_level.txt | 1 - 5 files changed, 61 deletions(-) delete mode 100644 QGL.egg-info/PKG-INFO delete mode 100644 QGL.egg-info/SOURCES.txt delete mode 100644 QGL.egg-info/dependency_links.txt delete mode 100644 QGL.egg-info/requires.txt delete mode 100644 QGL.egg-info/top_level.txt diff --git a/QGL.egg-info/PKG-INFO b/QGL.egg-info/PKG-INFO deleted file mode 100644 index ac9daf85..00000000 --- a/QGL.egg-info/PKG-INFO +++ /dev/null @@ -1,10 +0,0 @@ -Metadata-Version: 1.0 -Name: QGL -Version: 2.1 -Summary: UNKNOWN -Home-page: https://github.com/BBN-Q/QGL -Author: UNKNOWN -Author-email: UNKNOWN -License: UNKNOWN -Description: UNKNOWN -Platform: UNKNOWN diff --git a/QGL.egg-info/SOURCES.txt b/QGL.egg-info/SOURCES.txt deleted file mode 100644 index 79998f6f..00000000 --- a/QGL.egg-info/SOURCES.txt +++ /dev/null @@ -1,43 +0,0 @@ -README.md -setup.py -QGL/BlockLabel.py -QGL/ChannelLibraries.py -QGL/Channels.py -QGL/Cliffords.py -QGL/Compiler.py -QGL/ControlFlow.py -QGL/GSTTools.py -QGL/PatternUtils.py -QGL/Plotting.py -QGL/PulsePrimitives.py -QGL/PulseSequencePlotter.py -QGL/PulseSequencer.py -QGL/PulseShapes.py -QGL/Scheduler.py -QGL/TdmInstructions.py -QGL/Tomography.py -QGL/__init__.py -QGL/config.py -QGL/config_location.py -QGL/mm.py -QGL.egg-info/PKG-INFO -QGL.egg-info/SOURCES.txt -QGL.egg-info/dependency_links.txt -QGL.egg-info/requires.txt -QGL.egg-info/top_level.txt -QGL/BasicSequences/AllXY.py -QGL/BasicSequences/BlankingSweeps.py -QGL/BasicSequences/CR.py -QGL/BasicSequences/Decoupling.py -QGL/BasicSequences/Feedback.py -QGL/BasicSequences/FlipFlop.py -QGL/BasicSequences/RB.py -QGL/BasicSequences/Rabi.py -QGL/BasicSequences/SPAM.py -QGL/BasicSequences/T1T2.py -QGL/BasicSequences/__init__.py -QGL/BasicSequences/helpers.py -QGL/drivers/APS2Pattern.py -QGL/drivers/APSPattern.py -QGL/drivers/TekPattern.py -QGL/drivers/__init__.py \ No newline at end of file diff --git a/QGL.egg-info/dependency_links.txt b/QGL.egg-info/dependency_links.txt deleted file mode 100644 index 8b137891..00000000 --- a/QGL.egg-info/dependency_links.txt +++ /dev/null @@ -1 +0,0 @@ - diff --git a/QGL.egg-info/requires.txt b/QGL.egg-info/requires.txt deleted file mode 100644 index fc2fa0c8..00000000 --- a/QGL.egg-info/requires.txt +++ /dev/null @@ -1,6 +0,0 @@ -bbndb>=0.1 -numpy>=1.11.1 -scipy>=0.17.1 -networkx>=1.11 -h5py>=2.6.0 -bokeh>=0.12.13 diff --git a/QGL.egg-info/top_level.txt b/QGL.egg-info/top_level.txt deleted file mode 100644 index 0e533603..00000000 --- a/QGL.egg-info/top_level.txt +++ /dev/null @@ -1 +0,0 @@ -QGL From 15f0615fe8e1ca48f4731e4a514f67430fabe82a Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 26 Feb 2019 15:57:16 -0500 Subject: [PATCH 137/235] Added goto buttons and waveform plotters to APS2 disassembler --- QGL/drivers/APS2Pattern.py | 85 ++++++++++++++++++++++++++++++++++---- 1 file changed, 76 insertions(+), 9 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 12bbfddf..9b073f7a 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1492,6 +1492,23 @@ def read_instructions(filename): raw_instrs = raw_instructions(filename) return decompile_instructions(raw_instrs) +def read_waveforms(filename): + with open(filename, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + file_version = struct.unpack(' Date: Tue, 26 Feb 2019 21:34:53 -0500 Subject: [PATCH 138/235] Nice looking buttons for WFM and GOTO in disassembler --- QGL/drivers/APS2Pattern.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 9b073f7a..ee341326 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1595,7 +1595,6 @@ def __init__(self, instructions, waveforms): self.height = 1200 self.instructions = instructions self.waveforms = waveforms - print(self.waveforms) self.initUI() self.plotters = [] @@ -1634,17 +1633,19 @@ def createTable(self): def scroll_to_goto_target(row=target_row, tab=self.tableWidget): tab.scrollToItem(tab.item(row, 0)) btn.clicked.connect(scroll_to_goto_target) + btn.setStyleSheet("background-color: #006464; color: white; font-weight: bold; text-align: left;") self.tableWidget.setCellWidget(k, l, btn) if text == "WFM" and int(fields[4].split("=")[1])==0: # Not a TA pair btn = QPushButton(self.tableWidget) - btn.setText('WFM') + btn.setText(' WFM') addr = int(fields[6].split("=")[1]) count = int(fields[5].split("=")[1]) def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): w = MatplotlibWidget(I,Q) self.plotters.append(w) btn.clicked.connect(open_plotter) + btn.setStyleSheet("background-color: #00C800; color: white; font-weight: bold; text-align: left;") self.tableWidget.setCellWidget(k, l, btn) else: item = QTableWidgetItem(text) From b7e611ea4b268292434078898dcd865d019b0b96 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 27 Feb 2019 10:21:02 -0500 Subject: [PATCH 139/235] Move disassembler to utils directory --- QGL/drivers/APS2Pattern.py | 131 ------------------------------- utils/disassemble_aps.py | 157 +++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 131 deletions(-) create mode 100755 utils/disassemble_aps.py diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index ee341326..2cc0aa03 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1535,134 +1535,3 @@ def display_raw_instructions(raw): def display_raw_file(filename): raw = raw_instructions(filename) display_raw_instructions(raw) - -if __name__ == '__main__': - if len(sys.argv) == 2: - - from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton - from PyQt5.QtGui import QIcon, QColor, QFont - - from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - from matplotlib.figure import Figure - - table_font = QFont("Arial", weight=QFont.Bold) - - colors = {"WFM": QColor(0,200,0), - "GOTO": QColor(0,100,100), - "MARKER": QColor(150,150,200), - "CUSTOM": QColor(200,65,200), - "WRITEADDR": QColor(245, 105, 65), - "INVALIDATE": QColor(245, 105, 65), - "CALL": QColor(65, 205, 245), - "RET": QColor(65, 205, 245), - "LOADCMP": QColor(245, 225, 65), - "MODULATION": QColor(175, 255, 185)} - - class MatplotlibWidget(QWidget): - def __init__(self, I, Q, parent=None): - super(MatplotlibWidget, self).__init__(parent) - self.title = 'Waveform' - self.left = 100 - self.top = 100 - self.width = 800 - self.height = 600 - self.setWindowTitle(self.title) - self.setGeometry(self.left, self.top, self.width, self.height) - - self.figure = Figure() - self.canvas = FigureCanvasQTAgg(self.figure) - - self.axis = self.figure.add_subplot(111) - self.axis.plot(I) - self.axis.plot(Q) - self.layout = QVBoxLayout(self) - self.layout.addWidget(self.canvas) - self.setLayout(self.layout) - self.canvas.draw() - self.show() - - - class App(QWidget): - - COLUMN_COUNT = 7 - - def __init__(self, instructions, waveforms): - super().__init__() - self.title = 'APS2 Disassembled Instructions' - self.left = 100 - self.top = 100 - self.width = 1000 - self.height = 1200 - self.instructions = instructions - self.waveforms = waveforms - self.initUI() - self.plotters = [] - - def initUI(self): - self.setWindowTitle(self.title) - self.setGeometry(self.left, self.top, self.width, self.height) - - self.createTable() - self.layout = QVBoxLayout() - self.layout.addWidget(self.tableWidget) - self.setLayout(self.layout) - - # Show widget - self.show() - - def createTable(self): - # Create table - self.tableWidget = QTableWidget() - self.tableWidget.setRowCount(len(self.instructions)) - self.tableWidget.setColumnCount(self.COLUMN_COUNT) - - for k, instr in enumerate(self.instructions): - fields = str(instr).replace(',','').replace(';', '').split(" ") - if "|" in fields: - fields.remove("|") - if fields[0] in colors: - color = colors[fields[0]] - else: - color = None - for l, f in enumerate(fields): - text = fields[l] - if text == "GOTO": - btn = QPushButton(self.tableWidget) - btn.setText('GOTO') - target_row = int(fields[1].split("=")[1]) - def scroll_to_goto_target(row=target_row, tab=self.tableWidget): - tab.scrollToItem(tab.item(row, 0)) - btn.clicked.connect(scroll_to_goto_target) - btn.setStyleSheet("background-color: #006464; color: white; font-weight: bold; text-align: left;") - self.tableWidget.setCellWidget(k, l, btn) - if text == "WFM" and int(fields[4].split("=")[1])==0: - # Not a TA pair - btn = QPushButton(self.tableWidget) - btn.setText(' WFM') - addr = int(fields[6].split("=")[1]) - count = int(fields[5].split("=")[1]) - def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): - w = MatplotlibWidget(I,Q) - self.plotters.append(w) - btn.clicked.connect(open_plotter) - btn.setStyleSheet("background-color: #00C800; color: white; font-weight: bold; text-align: left;") - self.tableWidget.setCellWidget(k, l, btn) - else: - item = QTableWidgetItem(text) - item.setFont(table_font) - if color: - item.setBackground(color) - self.tableWidget.setItem(k, l, item) - if l < self.COLUMN_COUNT-1: - for j in range(l+1, self.COLUMN_COUNT): - item = QTableWidgetItem("") - if color: - item.setBackground(color) - self.tableWidget.setItem(k, j, item) - - self.tableWidget.move(0,0) - self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) - - app = QApplication(sys.argv[:1]) - ex = App(read_instructions(sys.argv[1]), read_waveforms(sys.argv[1])) - sys.exit(app.exec_()) diff --git a/utils/disassemble_aps.py b/utils/disassemble_aps.py new file mode 100755 index 00000000..1b7919c4 --- /dev/null +++ b/utils/disassemble_aps.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python + +import numpy as np +import sys +import os.path + +from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton +from PyQt5.QtGui import QIcon, QColor, QFont +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg +from matplotlib.figure import Figure + +colors = {"WFM": QColor(0,200,0), + "GOTO": QColor(0,100,100), + "MARKER": QColor(150,150,200), + "CUSTOM": QColor(200,65,200), + "WRITEADDR": QColor(245, 105, 65), + "INVALIDATE": QColor(245, 105, 65), + "CALL": QColor(65, 205, 245), + "RET": QColor(65, 205, 245), + "LOADCMP": QColor(245, 225, 65), + "MODULATION": QColor(175, 255, 185)} + +table_font = QFont("Arial", weight=QFont.Bold) + +class MatplotlibWidget(QWidget): + def __init__(self, I, Q, parent=None): + super(MatplotlibWidget, self).__init__(parent) + self.title = 'Waveform' + self.left = 100 + self.top = 100 + self.width = 800 + self.height = 600 + self.setWindowTitle(self.title) + self.setGeometry(self.left, self.top, self.width, self.height) + + self.figure = Figure() + self.canvas = FigureCanvasQTAgg(self.figure) + + self.axis = self.figure.add_subplot(111) + self.axis.plot(I) + self.axis.plot(Q) + self.layout = QVBoxLayout(self) + self.layout.addWidget(self.canvas) + self.setLayout(self.layout) + self.canvas.draw() + self.show() + + +class DisassemblerApp(QWidget): + + COLUMN_COUNT = 7 + + def __init__(self, instructions, waveforms): + super().__init__() + self.title = 'APS2 Disassembled Instructions' + self.left = 100 + self.top = 100 + self.width = 1000 + self.height = 1200 + self.instructions = instructions + self.waveforms = waveforms + self.initUI() + self.plotters = [] + + def initUI(self): + self.setWindowTitle(self.title) + self.setGeometry(self.left, self.top, self.width, self.height) + + self.createTable() + self.layout = QVBoxLayout() + self.layout.addWidget(self.tableWidget) + self.setLayout(self.layout) + + # Show widget + self.show() + + def createTable(self): + # Create table + self.tableWidget = QTableWidget() + self.tableWidget.setRowCount(len(self.instructions)) + self.tableWidget.setColumnCount(self.COLUMN_COUNT) + + for k, instr in enumerate(self.instructions): + fields = str(instr).replace(',','').replace(';', '').split(" ") + if "|" in fields: + fields.remove("|") + if fields[0] in colors: + color = colors[fields[0]] + else: + color = None + for l, f in enumerate(fields): + text = fields[l] + if text == "GOTO": + btn = QPushButton(self.tableWidget) + btn.setText('GOTO') + target_row = int(fields[1].split("=")[1]) + def scroll_to_goto_target(row=target_row, tab=self.tableWidget): + tab.scrollToItem(tab.item(row, 0)) + btn.clicked.connect(scroll_to_goto_target) + btn.setStyleSheet("background-color: #006464; color: white; font-weight: bold; text-align: left;") + self.tableWidget.setCellWidget(k, l, btn) + if text == "WFM" and int(fields[4].split("=")[1])==0: + # Not a TA pair + btn = QPushButton(self.tableWidget) + btn.setText(' WFM') + addr = int(fields[6].split("=")[1]) + count = int(fields[5].split("=")[1]) + def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): + w = MatplotlibWidget(I,Q) + self.plotters.append(w) + btn.clicked.connect(open_plotter) + btn.setStyleSheet("background-color: #00C800; color: white; font-weight: bold; text-align: left;") + self.tableWidget.setCellWidget(k, l, btn) + else: + item = QTableWidgetItem(text) + item.setFont(table_font) + if color: + item.setBackground(color) + self.tableWidget.setItem(k, l, item) + if l < self.COLUMN_COUNT-1: + for j in range(l+1, self.COLUMN_COUNT): + item = QTableWidgetItem("") + if color: + item.setBackground(color) + self.tableWidget.setItem(k, j, item) + + self.tableWidget.move(0,0) + self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) + +def get_target_hardware(filename): + with open(filename, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + return target_hw + +if __name__ == '__main__': + if len(sys.argv) == 2: + filename = sys.argv[1] + try: + hw = get_target_hardware(filename) + if hw == "APS2": + from QGL.drivers.APS2Pattern import read_instructions, read_waveforms + app = QApplication(sys.argv[:1]) + ex = DisassemblerApp(read_instructions(sys.argv[1]), read_waveforms(sys.argv[1])) + sys.exit(app.exec_()) + elif hw == "APS1": + print("APS1 disassmbler not yet implemented") + else: + raise Exception("Unknown hardware target.") + except: + print(f"The supplied file {filename} does not appear to be APS1 or APS2 format.") + + else: + print("Expected single file (.aps1 or .aps2 format) as an argument.") + + + + From b34d547356c56b7947ebfe3baeb831d40bf15589 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Wed, 27 Feb 2019 11:03:42 -0500 Subject: [PATCH 140/235] Add new_marker function --- QGL/ChannelLibraries.py | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 399f6441..63bf3faa 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -185,6 +185,9 @@ def qubits(self): def meas(self): return self.ent_by_type(Channels.Measurement) + def markers(self): + return self.ent_by_type(Channels.LogicalMarkerChannel) + @check_session_dirty def load(self, name, index=1): """Load the latest instance for a particular name. Specifying index = 2 will select the second most recent instance """ @@ -392,6 +395,12 @@ def new_qubit(self, label, **kwargs): self.add_and_update_dict(thing) return thing + @check_for_duplicates + def new_marker(self, label, phys_chan, **kwargs): + thing = Channels.LogicalMarkerChannel(label=label, phys_chan = phys_chan, channel_db=self.channelDatabase, **kwargs) + self.add_and_update_dict(thing) + return thing + @check_for_duplicates def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, reference='10MHz'): thing = Channels.Generator(label=label, model=model, address=address, power=power, @@ -401,7 +410,7 @@ def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, refere return thing def set_control(self, qubit_or_edge, transmitter, generator=None): - + if isinstance(transmitter, Channels.Transmitter): quads = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalQuadratureChannel)] markers = [c for c in transmitter.channels if isinstance(c, Channels.PhysicalMarkerChannel)] From 198c1c58e8325fe2fa7ffbd7b63535a7b25560d9 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 27 Feb 2019 12:13:46 -0500 Subject: [PATCH 141/235] set_measure and set_control can now be run multiple times --- QGL/ChannelLibraries.py | 22 ++++++++++++++++------ 1 file changed, 16 insertions(+), 6 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 63bf3faa..ff598de3 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -78,7 +78,7 @@ def check_for_duplicates(f): @wraps(f) def wrapper(cls, label, *args, **kwargs): if label in cls.channelDict: - logger.warning(f"Cannot create {label}: a channel with the same name already exists.") + logger.info(f"A database item with the name {label} already exists. Returning this existing item instead.") return cls.channelDict[label] else: return f(cls, label, *args, **kwargs) @@ -461,7 +461,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") if f"M-{qubit.label}" in self.channelDict: - logger.warning(f"Cannot create Measurement M-{qubit.label}: a channel with the same name already exists.") + logger.info(f"The measurement M-{qubit.label} already exists: using this measurement.") meas = self.channelDict[f"M-{qubit.label}"] else: meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=self.channelDatabase) @@ -471,9 +471,12 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("m1") - trig_chan = Channels.LogicalMarkerChannel(label=f"ReceiverTrig-{qubit.label}", channel_db=self.channelDatabase) - # print(phys_trig_channel.id, trig_chan.id) - self.session.add(trig_chan) + if f"ReceiverTrig-{qubit.label}" in self.channelDict: + logger.info(f"The Receiver trigger ReceiverTrig-{qubit.label} already exists: using this channel.") + trig_chan = self.channelDict[f"ReceiverTrig-{qubit.label}"] + else: + trig_chan = Channels.LogicalMarkerChannel(label=f"ReceiverTrig-{qubit.label}", channel_db=self.channelDatabase) + self.session.add(trig_chan) trig_chan.phys_chan = phys_trig_channel trig_chan.pulse_params = {"length": trigger_length, "shape_fun": "constant"} meas.trig_chan = trig_chan @@ -493,6 +496,9 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe if gate: phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("m2") + if f"M-{qubit.label}-gate" in self.channelDict: + logger.info(f"The gate channel M-{qubit.label}-gate already exists: using this channel.") + gate_chan = self.channelDict[f"M-{qubit.label}-gate"] gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=self.channelDatabase) gate_chan.phys_chan = phys_gate_channel meas.gate_chan = gate_chan @@ -508,7 +514,11 @@ def set_master(self, master_instrument, trig_channel=None, pulse_length=1e-7): if not isinstance(trig_channel, Channels.PhysicalMarkerChannel): raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") - st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=self.channelDatabase) + if "slave_trig" in self.channelDict: + logger.info(f"The slave trigger slave_trig already exists: using this trigger.") + st = self.channelDict["slave_trig"] + else: + st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=self.channelDatabase) st.phys_chan = trig_channel st.pulse_params = {"length": pulse_length, "shape_fun": "constant"} master_instrument.master = True From 9ee829f883e983b63baaec54f4ae3028817fd0b5 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:54:39 -0500 Subject: [PATCH 142/235] Add jump table indicator to block label. --- QGL/BlockLabel.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/QGL/BlockLabel.py b/QGL/BlockLabel.py index 52db8b63..6725cd13 100644 --- a/QGL/BlockLabel.py +++ b/QGL/BlockLabel.py @@ -2,8 +2,10 @@ class BlockLabel(object): - def __init__(self, label): + def __init__(self, label, jump_table=False, table_size=0): self.label = label + self.jump_table = jump_table + self.table_size = table_size def __repr__(self): return self.__str__() From b7932f93ebfdf9c648d66e7bfa088afeebdaa516 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:56:09 -0500 Subject: [PATCH 143/235] Only set up clifford generator once per sequence file. --- QGL/RandomCliffordTools.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index d4fc97bb..9317b3e2 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -33,13 +33,15 @@ from .APS2CustomInstructions import * from .Cliffords import C1, inverse_clifford, clifford_multiply +default_clifford_options = {"offset": 0x0, "spacing": 0x1, "seed": 0x31} + VALID_CLIFFORD_TYPES = ('RandomAC',) def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" if jt_label is None: - jt_label = BlockLabel.BlockLabel("JT") + jt_label = BlockLabel.BlockLabel("JT", jump_table=True, table_size=48) if not isinstance(jt_label, BlockLabel.BlockLabel): raise ValueError("Jump table label must be a BlockLabel.") @@ -55,14 +57,22 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): cliff_wires.insert(0, jump_table) + return jt_label + def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, - inv_addr=0x3, inv_values=[]): + inv_addr=0x3, inv_values=[], + clifford_options=default_clifford_options): if jt_label is None: jt_label = BlockLabel.BlockLabel("JT") if not isinstance(jt_label, BlockLabel.BlockLabel): raise ValueError("Jump table label must be a BlockLabel.") + #Insert defaults + for k,v in default_clifford_options.items(): + if k not in clifford_options.keys(): + cliford_options[k] = v + for idx, seq in enumerate(seqs[:]): if not PatternUtils.contains_runtime_pulses(seq): continue @@ -74,18 +84,14 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, and pulse.label in VALID_CLIFFORD_TYPES: has_random_cliff = True new_seq.extend(RandomClifford(jt_label, cliff_addr)) - print("Inserting clifford pulse!") + #print("Inserting clifford pulse!") else: new_seq.append(pulse) - info_seqs = [] - info_seqs.extend(RandomCliffordSetOffset(0x1, 0x0)) - info_seqs.extend(RandomCliffordSetSpacing(0x2, 0x1)) - - if isinstance(seq[0], BlockLabel.BlockLabel): - new_seq[1:1] = info_seqs - else: - new_seq[0:0] = info_seqs + # if isinstance(seq[0], BlockLabel.BlockLabel): + # new_seq[1:1] = info_seqs + # else: + # new_seq[0:0] = info_seqs if add_inv: #insert reset after first wait @@ -99,6 +105,12 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, seqs[idx] = new_seq + info_seqs = [] + info_seqs.extend(RandomCliffordSetOffset(0x1, clifford_options["offset"])) + info_seqs.extend(RandomCliffordSetSpacing(0x2, clifford_options["spacing"])) + info_seqs.extend(RandomCliffordSeed(clifford_options["seed"])) + seqs.insert(0, info_seqs) + def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): From 146ed60b0a96c8dbadce4ad099f5ce2e93d2772c Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:56:42 -0500 Subject: [PATCH 144/235] Correct __str__ for indicrect CALL --- QGL/ControlFlow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index cc1e6327..fed08018 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -155,7 +155,7 @@ def __init__(self, target, indirect=False): self.indirect = indirect def __str__(self): - if self.load_addr: + if not self.indirect: return f"{self.instruction}({str(self.target)})" else: return f"{self.instruction}({str(self.target)}, INDIRECT)" From 221609e5c458a3077802b7e6645b09b937ed5a5f Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:57:20 -0500 Subject: [PATCH 145/235] Correct implementation of APS2_SET_SEED --- QGL/APS2CustomInstructions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 1d93cf38..ae9c4221 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -48,14 +48,14 @@ def RandomCliffordSetOffset(addr, offset): return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, offset, tdm=False), - LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0x3)] + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), # + CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0xA)] def RandomCliffordSetSpacing(addr, spacing): return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, spacing, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0x3)] + CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0xA)] def RandomClifford(target, addr): return [Invalidate(addr, 0, tdm=False), @@ -73,4 +73,7 @@ def RandomCliffordInverseReset(addr): #for now don't use addr return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) def RandomCliffordSeed(seed): - raise NotImplementedError("APS_CLIFFORD_SET_SEED not implemented!") + return [Invalidate(0xB, 0, tdm=False), + WriteAddr(0xB, seed, tdm=False), + LoadCmpVram(0xB, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_SET_SEED", 0xB, 0xA)] From 86ab8ba5a2957da18f74febe8d19e71207527408 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:58:15 -0500 Subject: [PATCH 146/235] Keep modulation commands explicitly in subroutines to prevent eliding Z pulses --- QGL/Compiler.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 57abd552..35a59b49 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -37,6 +37,7 @@ from . import BlockLabel from . import TdmInstructions # only for APS2-TDM from . import APS2CustomInstructions +from .RandomCliffordTools import default_clifford_options from . import RandomCliffordTools import gc @@ -365,7 +366,8 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords=False): +def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords=False, + clifford_options=default_clifford_options): ChannelLibraries.channelLib.update_channelDict() clear_pulse_cache() @@ -402,7 +404,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= # Compile all the pulses/pulseblocks to sequences of pulses and control flow logger.info("Compiling sequences.") - wireSeqs = compile_sequences(seqs, channels, random_cliffords=random_cliffords) + wireSeqs = compile_sequences(seqs, channels, random_cliffords=random_cliffords, clifford_options=clifford_options) if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") @@ -593,7 +595,8 @@ def compile_to_hardware(seqs, return metafilepath -def compile_sequences(seqs, channels=set(), random_cliffords=False): +def compile_sequences(seqs, channels=set(), random_cliffords=False, + clifford_options=default_clifford_options): ''' Main function to convert sequences to miniLL's and waveform libraries. ''' @@ -608,14 +611,21 @@ def compile_sequences(seqs, channels=set(), random_cliffords=False): seqs += subroutines if random_cliffords: + qubits = [q for q in list(channels) if isinstance(q, Channels.Qubit)] if len(qubits) > 1: raise Exception("Too many qubits! Don't know how to deal with this yet.") #replace cliffords with cliffords = [[get_clifford_type(seqs)(n)] for n in range(24)] - RandomCliffordTools.generate_clifford_jump_table(cliffords) - RandomCliffordTools.insert_clifford_calls(seqs) + jt_label = RandomCliffordTools.generate_clifford_jump_table(cliffords) + RandomCliffordTools.insert_clifford_calls(seqs, jt_label=jt_label, clifford_options=clifford_options) + + # if not isinstance(seqs[0][0], BlockLabel.BlockLabel): + # raise Exception("Fist instruciton must be block label!") + # start_label = seqs[0][0] seqs += cliffords + #seqs[0:0] = cliffords + #seqs[0].insert(0, ControlFlow.Goto(start_label)) #expand the channel definitions for anything defined in subroutines for func in subroutines: @@ -701,7 +711,14 @@ def flatten_to_pulses(obj): wires = propagate_node_frame_to_edges( wires, chan, block.pulses[chan].frameChange) # drop length 0 blocks but push nonzero frame changes onto previous entries - if block.length == 0: + + is_subroutine = False + if isinstance(seq[-1], ControlFlow.Return): + #Make sure we do not drop frame update instructions for subroutines that are only Z pulses + is_subroutine = True + + + if block.length == 0 and not is_subroutine: for chan in channels: if block.pulses[chan].frameChange == 0: continue From ed8d087872fc231b1a8061d39857d3197a6e4c63 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:59:01 -0500 Subject: [PATCH 147/235] Proper prefetching for jump-table based subroutines --- QGL/drivers/APS2Pattern.py | 43 ++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 12bbfddf..b067a83d 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -297,8 +297,11 @@ def __str__(self): elif any( [instrOpCode == op for op in [GOTO, CALL, RET, REPEAT, PREFETCH]]): + if self.use_ram: + out += " RAM" out += " | target_addr={}".format(self.payload & 2**26 - 1) + elif instrOpCode == LOAD: out += " | count={}".format(self.payload) @@ -359,6 +362,9 @@ def __ne__(self, other): def __hash__(self): return hash((self.header, self.payload, self.label)) + def isa(self, value): + return (self.header >> 4) == value + @property def address(self): return self.payload & 0xffffffff # bottom 32-bits of payload @@ -616,7 +622,7 @@ def to_instruction(self, write_flag=True, label=None): instr.writeFlag = write_flag return instr -def inject_modulation_cmds(seqs, force_phase_update=False): +def inject_modulation_cmds(seqs): """ Inject modulation commands from phase, frequency and frameChange of waveforms in an IQ waveform sequence. Assume up to 2 NCOs for now. @@ -634,13 +640,17 @@ def inject_modulation_cmds(seqs, force_phase_update=False): frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] no_frame_cmds = np.all(np.less(np.abs(frame_changes), 1e-8)) no_modulation_cmds = no_freq_cmds and no_phase_cmds and no_frame_cmds - #import pdb; pdb.set_trace() if no_modulation_cmds: continue mod_seq = [] pending_frame_update = False + #check to see if we are in a subroutine by using last instruction as return as a tell + #if so, ensure frame updates do not get dropped + if isinstance(seq[-1], ControlFlow.Return): + force_phase_update = True + for entry in seq: #copies to avoid same object having different timestamps later @@ -728,7 +738,6 @@ def synchronize_clocks(seqs): syncInstructions = [list(filter( lambda s: isinstance(s, ControlFlow.ControlInstruction), seq)) for seq in seqs if seq] - #import pdb; pdb.set_trace() # Add length to control-flow instructions to make accumulated time match at end of CFI. # Keep running tally of how much each channel has been shifted so far. localShift = [0 for _ in syncInstructions] @@ -847,14 +856,14 @@ def create_seq_instructions(seqs, offsets, label = None): instructions.append(LoadCmpVram(entry.addr, entry.mask, label=label)) # some TDM instructions are ignored by the APS elif isinstance(entry, TdmInstructions.CustomInstruction): - print(str(entry)) + #print(str(entry)) try: instructions.append(Custom(entry.in_addr, entry.out_addr, APS2_CUSTOM[entry.instruction], label=label)) except KeyError as e: raise Exception(f"Got unknown APS2 instruction: {e.args[0]} in {str(entry)}.") elif isinstance(entry, TdmInstructions.WriteAddrInstruction): - print(str(entry)) + #print(str(entry)) if entry.instruction == 'INVALIDATE' and entry.tdm == False: instructions.append(Invalidate(entry.addr, entry.value, label=label)) if entry.instruction == 'WRITEADDR' and entry.tdm == False: @@ -939,7 +948,6 @@ def create_instr_data(seqs, offsets, cache_lines): except: pass instructions += seq - #if we have any subroutines then group in cache lines if subroutines_start >= 0: subroutine_instrs = [] @@ -967,12 +975,25 @@ def create_instr_data(seqs, offsets, cache_lines): CACHE_LINE_LENGTH)) #inject prefetch commands before waits wait_idx = [idx for idx, instr in enumerate(instructions) - if (instr.header >> 4) == WAIT] + [len(instructions)] + if instr.isa(WAIT)] + [len(instructions)] instructions_with_prefetch = instructions[:wait_idx[0]] last_prefetch = None + for start, stop in zip(wait_idx[:-1], wait_idx[1:]): call_targets = [instr.target for instr in instructions[start:stop] - if (instr.header >> 4) == CALL] + if instr.isa(CALL)] + + #Check if we call into any jump tables + jump_tables = [label for label in call_targets if label.jump_table] + if len(jump_tables) > 0: + for jt in set(jump_tables): + #Find the start of the jump table + start_idx = next(j for j, instr in enumerate(subroutine_instrs) + if instr.label == jt) + stop_idx = start_idx + jt.table_size + call_targets.extend([instr.target for instr in + subroutine_instrs[start_idx:stop_idx] + if instr.isa(CALL)]) needed_lines = set() for target in call_targets: needed_lines.add(subroutine_cache_line[target]) @@ -989,9 +1010,9 @@ def create_instr_data(seqs, offsets, cache_lines): #pad out instruction vector to ensure circular cache never loads a subroutine pad_instrs = 7 * 128 + (128 - ((len(instructions) + 128) % 128)) instructions += [NoOp()] * pad_instrs - instructions += subroutine_instrs + #turn symbols into integers addresses resolve_symbols(instructions) @@ -1006,7 +1027,7 @@ def resolve_symbols(seq): labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] symbols = {label: idx for idx, label in labeled_entries} - print(f"Found labels: {symbols}") + #print(f"Found labels: {symbols}") for entry in seq: if entry.target is not None and entry.target in symbols.keys(): entry.address = symbols[entry.target] @@ -1207,7 +1228,7 @@ def start_new_seq(): chan_select_bits = ((instr.header >> 2) & 0x1, (instr.header >> 3) & 0x1) #On older firmware we broadcast by default whereas on newer we respect the engine select - for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits): + for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits):plot_pulse if (file_version < 4) or select_bit: if isTA: seqs[chan][-1].append((count, wf_lib[chan][addr])) From 0cbcd575e2c69da01af924369c5b46b2e77a403d Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 17:00:52 -0500 Subject: [PATCH 148/235] Fix typo. --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 1f78cba3..bcdfbbd5 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1228,7 +1228,7 @@ def start_new_seq(): chan_select_bits = ((instr.header >> 2) & 0x1, (instr.header >> 3) & 0x1) #On older firmware we broadcast by default whereas on newer we respect the engine select - for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits):plot_pulse + for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits): if (file_version < 4) or select_bit: if isTA: seqs[chan][-1].append((count, wf_lib[chan][addr])) From 1160bad34142a4888b8186fcc37239a227cf253a Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 27 Feb 2019 17:04:39 -0500 Subject: [PATCH 149/235] More flexible about set_measure and set_control, which can now be rerun with expected results --- QGL/ChannelLibraries.py | 44 ++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 22 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index ff598de3..38b3d1cd 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -56,7 +56,7 @@ channelLib = None -logger = logging.getLogger(__name__) +logger = logging.getLogger("QGL") def check_session_dirty(f): """Since we can't mix db objects from separate sessions, re-fetch entities by their unique IDs""" @@ -78,7 +78,7 @@ def check_for_duplicates(f): @wraps(f) def wrapper(cls, label, *args, **kwargs): if label in cls.channelDict: - logger.info(f"A database item with the name {label} already exists. Returning this existing item instead.") + logger.warning(f"A database item with the name {label} already exists. Returning this existing item instead.") return cls.channelDict[label] else: return f(cls, label, *args, **kwargs) @@ -285,14 +285,14 @@ def build_connectivity_graph(self): self.connectivityG[chan.source][chan.target]['channel'] = chan @check_for_duplicates - def new_APS2(self, label, address): + def new_APS2(self, label, address, **kwargs): chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) m1 = Channels.PhysicalMarkerChannel(label=f"{label}-m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) m2 = Channels.PhysicalMarkerChannel(label=f"{label}-m2", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) m3 = Channels.PhysicalMarkerChannel(label=f"{label}-m3", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) m4 = Channels.PhysicalMarkerChannel(label=f"{label}-m4", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan1, m1, m2, m3, m4], channel_db=self.channelDatabase) + this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan1, m1, m2, m3, m4], channel_db=self.channelDatabase, **kwargs) this_transmitter.trigger_source = "external" this_transmitter.address = address @@ -300,7 +300,7 @@ def new_APS2(self, label, address): return this_transmitter @check_for_duplicates - def new_APS(self, label, address): + def new_APS(self, label, address, **kwargs): chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) chan2 = Channels.PhysicalQuadratureChannel(label=f"{label}-34", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) m1 = Channels.PhysicalMarkerChannel(label=f"{label}-1m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) @@ -316,25 +316,25 @@ def new_APS(self, label, address): return this_transmitter @check_for_duplicates - def new_TDM(self, label, address): + def new_TDM(self, label, address, **kwargs): return Channels.Processor(label=label, model="TDM", address=address, trigger_interval=250e-6) @check_for_duplicates - def new_spectrum_analzyer(self, label, address, source): - sa = Channels.SpectrumAnalyzer(label=label, model="SpectrumAnalyzer", address=address, LO_source=source, channel_db=self.channelDatabase) + def new_spectrum_analzyer(self, label, address, source, **kwargs): + sa = Channels.SpectrumAnalyzer(label=label, model="SpectrumAnalyzer", address=address, LO_source=source, channel_db=self.channelDatabase, **kwargs) self.add_and_update_dict(sa) return sa @check_for_duplicates - def new_DC_source(self, label, address): - dcsource = Channels.DCSource(label=label, model="YokogawaGS200", address=address, standalone=True, channel_db=self.channelDatabase) + def new_DC_source(self, label, address, **kwargs): + dcsource = Channels.DCSource(label=label, model="YokogawaGS200", address=address, standalone=True, channel_db=self.channelDatabase, **kwargs) self.add_and_update_dict(dcsource) return dcsource @check_for_duplicates - def new_APS2_rack(self, label, ip_addresses, tdm_ip=None): + def new_APS2_rack(self, label, ip_addresses, tdm_ip=None, **kwargs): transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] - this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase) + this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase, **kwargs) for t in transmitters: t.transceiver = this_transceiver @@ -348,7 +348,7 @@ def new_APS2_rack(self, label, ip_addresses, tdm_ip=None): return this_transceiver @check_for_duplicates - def new_X6(self, label, address, dsp_channel=0, record_length=1024): + def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): phys_channels = (1, 2) dsp_channels = (1, 2) @@ -362,7 +362,7 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024): channel_db=self.channelDatabase)) this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=chans, - record_length=record_length, channel_db=self.channelDatabase) + record_length=record_length, channel_db=self.channelDatabase, **kwargs) this_receiver.trigger_source = "external" this_receiver.stream_types = "raw, demodulated, integrated" this_receiver.address = address @@ -376,12 +376,12 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024): return this_receiver @check_for_duplicates - def new_Alazar(self, label, address, record_length=1024): + def new_Alazar(self, label, address, record_length=1024, **kwargs): chan1 = Channels.ReceiverChannel(label=f"RecvChan-{label}-1", channel=1, channel_db=self.channelDatabase) chan2 = Channels.ReceiverChannel(label=f"RecvChan-{label}-2", channel=2, channel_db=self.channelDatabase) this_receiver = Channels.Receiver(label=label, model="AlazarATS9870", address=address, channels=[chan1, chan2], - record_length=record_length, channel_db=self.channelDatabase) + record_length=record_length, channel_db=self.channelDatabase, **kwargs) this_receiver.trigger_source = "external" this_receiver.stream_types = "raw" this_receiver.address = address @@ -402,10 +402,10 @@ def new_marker(self, label, phys_chan, **kwargs): return thing @check_for_duplicates - def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, reference='10MHz'): + def new_source(self, label, model, address, power=-30.0, frequency=5.0e9, reference='10MHz', **kwargs): thing = Channels.Generator(label=label, model=model, address=address, power=power, frequency=frequency, reference=reference, - channel_db=self.channelDatabase) + channel_db=self.channelDatabase, **kwargs) self.add_and_update_dict(thing) return thing @@ -461,7 +461,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") if f"M-{qubit.label}" in self.channelDict: - logger.info(f"The measurement M-{qubit.label} already exists: using this measurement.") + logger.warning(f"The measurement M-{qubit.label} already exists: using this measurement.") meas = self.channelDict[f"M-{qubit.label}"] else: meas = Channels.Measurement(label=f"M-{qubit.label}", channel_db=self.channelDatabase) @@ -472,7 +472,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe phys_trig_channel = trig_channel if trig_channel else transmitter.get_chan("m1") if f"ReceiverTrig-{qubit.label}" in self.channelDict: - logger.info(f"The Receiver trigger ReceiverTrig-{qubit.label} already exists: using this channel.") + logger.warning(f"The Receiver trigger ReceiverTrig-{qubit.label} already exists: using this channel.") trig_chan = self.channelDict[f"ReceiverTrig-{qubit.label}"] else: trig_chan = Channels.LogicalMarkerChannel(label=f"ReceiverTrig-{qubit.label}", channel_db=self.channelDatabase) @@ -497,7 +497,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe if gate: phys_gate_channel = gate_channel if gate_channel else transmitter.get_chan("m2") if f"M-{qubit.label}-gate" in self.channelDict: - logger.info(f"The gate channel M-{qubit.label}-gate already exists: using this channel.") + logger.warning(f"The gate channel M-{qubit.label}-gate already exists: using this channel.") gate_chan = self.channelDict[f"M-{qubit.label}-gate"] gate_chan = Channels.LogicalMarkerChannel(label=f"M-{qubit.label}-gate", channel_db=self.channelDatabase) gate_chan.phys_chan = phys_gate_channel @@ -515,7 +515,7 @@ def set_master(self, master_instrument, trig_channel=None, pulse_length=1e-7): raise ValueError("In set_master the trigger channel must be an instance of PhysicalMarkerChannel") if "slave_trig" in self.channelDict: - logger.info(f"The slave trigger slave_trig already exists: using this trigger.") + logger.warning(f"The slave trigger slave_trig already exists: using this trigger.") st = self.channelDict["slave_trig"] else: st = Channels.LogicalMarkerChannel(label="slave_trig", channel_db=self.channelDatabase) From a0d37466332a901f4fbd6c2ac51d862f70fbae07 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 17:05:06 -0500 Subject: [PATCH 150/235] Remove debug watcher from QGL (now in libaps2) --- tests/aps_debug_watcher.py | 219 ------------------------------------- thing.txt | 3 + 2 files changed, 3 insertions(+), 219 deletions(-) delete mode 100644 tests/aps_debug_watcher.py create mode 100644 thing.txt diff --git a/tests/aps_debug_watcher.py b/tests/aps_debug_watcher.py deleted file mode 100644 index 4f389e8f..00000000 --- a/tests/aps_debug_watcher.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python - -import sys -import socket -import struct -try: - from QGL.drivers import APS2Pattern - haveQGL = True -except: - haveQGL = False -import numpy as np -from ansicolor import * # from ansicolors - -ip = sys.argv[1] -port = 0xbb50 - -short_time = True - -raw_mode = '-r' in sys.argv -tdm_mode = '-tdm' in sys.argv - -print("Connecting to APS Debug Port at {0}:{1}".format(ip,port)) - -PACKET_SIZE=168//8+1 - -if short_time: - PACKET_SIZE -= 2 - -MODE_UNKNOWN = 0 -MODE_SEQUENCER = 1 -MODE_RAM = 2 -MODE_TRIGGER = 3 - -def bittest(v, b): - if ((v & (1< 0: - extraData = data[block_size:] - data = data[:block_size] - - for cnt in range(num_packets): - start = cnt * PACKET_SIZE - end = (cnt+1)*PACKET_SIZE - packet = data[start:end] - packet_bytes = bytearray(packet) - if raw_mode: - print(packet_bytes) - continue - - if packet[0] == 0x1: - mode = MODE_SEQUENCER - elif packet[0] in [0x2,0x3, 0x4, 0x5]: - mode = MODE_RAM - elif packet[0] == 0x6: - mode = MODE_TRIGGER - else: - mode = MODE_UNKNOWN - - - if short_time: - uptime_seconds = packet_bytes[1:3] - uptime_nanoseconds = packet_bytes[3:7] - - uptime_seconds = struct.unpack(">H", uptime_seconds)[0] - else: - uptime_seconds = packet_bytes[1:5] - uptime_nanoseconds = packet_bytes[5:9] - - uptime_seconds = struct.unpack(">I", uptime_seconds)[0] - - uptime_nanoseconds = struct.unpack(">I", uptime_nanoseconds)[0] - - #print(uptime_seconds, uptime_nanoseconds) - - uptime_nanoseconds = uptime_nanoseconds/1e9 - uptime = uptime_seconds + uptime_nanoseconds - - if short_time: - start = 8 - else: - start = 10 - - if mode == MODE_SEQUENCER: - - if short_time: - triggerWord = packet[7] - else: - triggerWord = packet[9] - - haltBits = packet[start] - - haltStr = formatHaltBits(haltBits) - - instructionAddr, start = get_bytes(packet, start, 4) - instructionAddr = bytearray(instructionAddr) - # clear halt bits, only the first two bits are part of the Addr - instructionAddr[0] = instructionAddr[0] & 0x3 - instructionAddr = struct.unpack(">I", instructionAddr)[0] - - seq_debug_data, start = get_bytes(packet, start, 8) - seq_debug_data = np.fromstring(seq_debug_data[::-1], dtype=np.uint64) - - if haveQGL: - instruction = APS2Pattern.Instruction.unflatten(seq_debug_data, decode_as_tdm = tdm_mode) - else: - instruction = '' - - h = "{:016x}".format(seq_debug_data[0]).upper() - - print(red("{:10}".format("Sequencer")), \ - white(": {:4}".format(instructionAddr)),\ - blue("0x{}".format(h)), \ - yellow(" {:6.8f}".format(uptime)), \ - haltStr, \ - "T:0x{:x}".format(triggerWord), \ - " {} ".format(instruction)) - # if instructionAddr > 100: - # sys.exit() - elif mode == MODE_RAM: - - zeros, start = get_bytes(packet, start, 4) - vram_addr, start = get_bytes(packet, start, 4) - vram_data, start = get_bytes(packet, start, 4) - - vram_header = struct.unpack(">I", zeros)[0] - vram_addr = struct.unpack(">I", vram_addr)[0] - vram_data = struct.unpack(">I", vram_data)[0] - - if packet[0] == 0x2: - ram_mode = "Valid" - - if packet[0] == 0x3: - ram_mode = "Write" - - if packet[0] == 0x4: - ram_mode = "Send" - - if packet[0] == 0x5: - ram_mode = "Recv" - - print(green("{:10}".format("RAM")), \ - white(": {:>23}".format(ram_mode)),\ - yellow(" {:6.8f}".format(uptime)), \ - "addr = 0x{:08X} data = 0x{:08X}".format(vram_addr, vram_data)) - - elif mode == MODE_TRIGGER: - - zeros, start = get_bytes(packet, start, 8) - syncBits, start = get_bytes(packet,start,1) - haltBits, start = get_bytes(packet, start, 1) - triggers, start = get_bytes(packet, start, 1) - triggerWord, start = get_bytes(packet,start,1) - - for z in zeros: - if z != 0: - print("Error expected 0 not", z) - - haltBits = haltBits[0] - syncBits = syncBits[0] - triggers = triggers[0] - triggerWord = triggerWord[0] - - haltStr = formatHaltBits(haltBits) - - syncWF = (syncBits >> 3) & 0xF - syncMarker = (syncBits >> 1) & 0x3 - syncMod = syncBits & 0x1 - - - halt = bittest(syncBits,4) - pc_jump = bittest(syncBits, 5) - tready = bittest(syncBits, 6) - tvalid = bittest(syncBits, 7) - - syncStr = "TV: {} TR: {} PJ: {} H: {} SWF: 0x{} SMa: 0x{} SMo: 0x{}".format(tvalid, tready, pc_jump, halt, syncWF, syncMarker, syncMod) - - trigger = bittest(triggers,1) - triggerWordValid = bittest(triggers,0) - - triggerStr = "t = {} tWV = {} tW = 0x{:0X}".format(trigger,triggerWordValid, triggerWord) - - print(green("{:10}".format("Trigger")), \ - white(": {:>23}".format('')),\ - yellow(" {:6.8f}".format(uptime)), \ - haltStr, \ - syncStr, \ - triggerStr) - elif mode == MODE_UNKNOWN: - print(green("{:10}".format("Unknown")),packet_bytes) - -s.close() diff --git a/thing.txt b/thing.txt new file mode 100644 index 00000000..5e24153f --- /dev/null +++ b/thing.txt @@ -0,0 +1,3 @@ +1.222 4.0+5.0i +1.32 4.1+5.6i +1.55 4.2+6.7i From f45db34bb8ccebf7671f91c560250476d8e8bcda Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 17:05:49 -0500 Subject: [PATCH 151/235] Remove junk --- thing.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 thing.txt diff --git a/thing.txt b/thing.txt deleted file mode 100644 index 5e24153f..00000000 --- a/thing.txt +++ /dev/null @@ -1,3 +0,0 @@ -1.222 4.0+5.0i -1.32 4.1+5.6i -1.55 4.2+6.7i From bc0af46203def406cbb255ca4ae40e0dd9b3d61f Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 27 Feb 2019 17:06:00 -0500 Subject: [PATCH 152/235] AWGDir now defaults to temp directory if not defined in config --- QGL/config.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/QGL/config.py b/QGL/config.py index 18ca79c5..96950d95 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -4,9 +4,17 @@ import sys import re import importlib +import tempfile +import logging + +logger = logging.getLogger("QGL") # Where to store AWG data -AWGDir = None +if os.getenv('AWG_DIR'): + AWGDir = os.getenv('AWG_DIR') +else: + logger.warning("Defaulting to temporary directory for AWG sequence file outputs.") + AWGDir = tempfile.mkdtemp(prefix="AWG") # The db file, where the channel libraries are stored db_resource_name = None From 2546c3d05a6bb35b06142c41083b5423177bae84 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Fri, 1 Feb 2019 10:43:42 -0500 Subject: [PATCH 153/235] Fix alignment for equal-length pulses --- QGL/PulseSequencer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 315c3db8..3b260634 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -299,7 +299,7 @@ def rec_length(obj): pulse_list.append(pulse) if max(pad_lengths) == 0: # no padding element required - return pulses + return reduce(operator.mul, pulses) elif mode == 'left': return reduce(operator.mul,[p + TAPulse('Id', p.channel, max(pulse_lengths) - p.length,0) if p.length < max(pulse_lengths) else p for p in pulse_list]) elif mode == 'right': From 681407d67c9c55075dbcbd6329a46e34a05247b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Thu, 28 Feb 2019 19:10:56 -0500 Subject: [PATCH 154/235] Set if_freq for demod stream To match meas autodyne_freq --- QGL/ChannelLibraries.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 38b3d1cd..cc35531b 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -491,6 +491,8 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe else: raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") + if rcv_chan.stream_type == 'demodulated': + rcv_chan.if_freq = meas.autodyne_freq meas.receiver_chan = rcv_chan self.add_and_update_dict([meas, trig_chan]) From 3749e01b68b5cd7035fe29faa938090b9c437cee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Fri, 1 Mar 2019 13:24:29 -0500 Subject: [PATCH 155/235] Rever last commit See comments --- QGL/ChannelLibraries.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index cc35531b..bf83412e 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -490,9 +490,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe rcv_chan = receivers else: raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - - if rcv_chan.stream_type == 'demodulated': - rcv_chan.if_freq = meas.autodyne_freq + meas.receiver_chan = rcv_chan self.add_and_update_dict([meas, trig_chan]) From 8f09532863668c1b88063b6eb2d65206425913c9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Fri, 1 Mar 2019 15:30:44 -0500 Subject: [PATCH 156/235] Set default kernel --GR, DR --- QGL/ChannelLibraries.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index bf83412e..2c09fc7b 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -370,7 +370,7 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): # Add a default kernel for chan in chans: if chan.stream_type is "integrated": - chan.kernel = np.ones(record_length, dtype=np.complex).tobytes() + chan.kernel = np.ones(record_length) self.add_and_update_dict(this_receiver) return this_receiver @@ -490,7 +490,7 @@ def set_measure(self, qubit, transmitter, receivers, generator=None, trig_channe rcv_chan = receivers else: raise ValueError("In set_measure the Transmitter must have a single quadrature channel or a specific channel must be passed instead") - + meas.receiver_chan = rcv_chan self.add_and_update_dict([meas, trig_chan]) From 1fd852b61b75fa8c4800dda664d018bf6faca048 Mon Sep 17 00:00:00 2001 From: "T.J.Rogers" Date: Mon, 4 Mar 2019 15:56:24 -0500 Subject: [PATCH 157/235] Rolled back sqlalchemy version to 2.1.15 (vice 2.1.17) to use version already bbnqconda loaded. --- setup.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/setup.py b/setup.py index 569e446e..b2987a2d 100644 --- a/setup.py +++ b/setup.py @@ -10,5 +10,6 @@ "scipy >= 0.17.1", "networkx >= 1.11", "bqplot >= 0.11.5", - "sqlalchemy >= 1.2.17", + # "sqlalchemy >= 1.2.17", Rolling back 20 .15 per 27/2/2019 email + "sqlalchemy >= 1.2.15" ]) From 5cd1fd0ede04793b9f7be80b3cc98bc3d4018003 Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 8 Mar 2019 16:35:33 -0500 Subject: [PATCH 158/235] Add new_attenuator method -- with BD, GR, SF? --- QGL/ChannelLibraries.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 2c09fc7b..7e4a3db7 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -331,6 +331,15 @@ def new_DC_source(self, label, address, **kwargs): self.add_and_update_dict(dcsource) return dcsource + @check_for_duplicates + def new_attenuator(self,label,address,attenuation=0): + chan1 = Channels.AttenuatorChannel(label=f"AttenChan-{label}-1", channel=1, attenuation=attenuation, channel_db=self.channelDatabase) + chan2 = Channels.AttenuatorChannel(label=f"AttenChan-{label}-2", channel=2, attenuation=attenuation, channel_db=self.channelDatabase) + chan3 = Channels.AttenuatorChannel(label=f"AttenChan-{label}-3", channel=3, attenuation=attenuation, channel_db=self.channelDatabase) + thing = Channels.Attenuator(label=label,model="DigitalAttenuator",address=address,channels=[chan1, chan2, chan3], standalone=True, channel_db=self.channelDatabase) + self.add_and_update_dict(thing) + return thing + @check_for_duplicates def new_APS2_rack(self, label, ip_addresses, tdm_ip=None, **kwargs): transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] From 33cbf2e21bee4b0271ecc18d15494c4defb045bb Mon Sep 17 00:00:00 2001 From: "Graham E. Rowlands" Date: Tue, 12 Mar 2019 11:27:13 -0400 Subject: [PATCH 159/235] Ditch comment and upgrade requirements.txt for 1.2.15 --- requirements.txt | 2 +- setup.py | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/requirements.txt b/requirements.txt index 04eaafe5..31600f5c 100644 --- a/requirements.txt +++ b/requirements.txt @@ -3,4 +3,4 @@ numpy >= 1.11.1 scipy >= 0.17.1 networkx >= 1.11 bqplot >= 0.11.5 -sqlalchemy >= 1.2.17 \ No newline at end of file +sqlalchemy >= 1.2.15 \ No newline at end of file diff --git a/setup.py b/setup.py index b2987a2d..07db1d0b 100644 --- a/setup.py +++ b/setup.py @@ -10,6 +10,5 @@ "scipy >= 0.17.1", "networkx >= 1.11", "bqplot >= 0.11.5", - # "sqlalchemy >= 1.2.17", Rolling back 20 .15 per 27/2/2019 email "sqlalchemy >= 1.2.15" ]) From ca19e840899c1bb31f416be4774680702a4eb6a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Fri, 15 Mar 2019 15:14:02 -0400 Subject: [PATCH 160/235] Revert to old stream labels For consistency, I'd rather label first phys, then dsp channel --- QGL/ChannelLibraries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 7e4a3db7..ababe803 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -366,7 +366,7 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): chans = [] for p, d, s in itertools.product(phys_channels, dsp_channels, stream_types): - chans.append(Channels.ReceiverChannel(label=f"RecvChan-{label}-{s}-{d}-{p}", + chans.append(Channels.ReceiverChannel(label=f"RecvChan-{label}-{s}-{p}-{d}", channel=p, dsp_channel=d, stream_type=s, channel_db=self.channelDatabase)) From 5477c87ef89385106263ec81c274422c4cb49631 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Mon, 18 Mar 2019 16:39:23 -0400 Subject: [PATCH 161/235] Update dictionary for existing objects As opposed to ignoring any new instance --- QGL/ChannelLibraries.py | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 7e4a3db7..19dccdae 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -78,8 +78,9 @@ def check_for_duplicates(f): @wraps(f) def wrapper(cls, label, *args, **kwargs): if label in cls.channelDict: - logger.warning(f"A database item with the name {label} already exists. Returning this existing item instead.") - return cls.channelDict[label] + logger.warning(f"A database item with the name {label} already exists. Updating parameters of this existing item instead.") + cls.channelDict[label].__dict__.update(kwargs) + return cls.channelDict[label] #should check for difference in args else: return f(cls, label, *args, **kwargs) return wrapper @@ -381,6 +382,7 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): if chan.stream_type is "integrated": chan.kernel = np.ones(record_length) + self.add_and_update_dict(this_receiver) return this_receiver From 956272d46d883c9908c96e75d5655f335fdb14d9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Mon, 18 Mar 2019 16:40:18 -0400 Subject: [PATCH 162/235] Fix Rabi with no cals --- QGL/BasicSequences/Rabi.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/QGL/BasicSequences/Rabi.py b/QGL/BasicSequences/Rabi.py index 3acb5329..c65028b2 100644 --- a/QGL/BasicSequences/Rabi.py +++ b/QGL/BasicSequences/Rabi.py @@ -108,6 +108,8 @@ def RabiAmp_NQubits(qubits, if docals: seqs += create_cal_seqs(qubits, calRepeats, measChans=measChans) + else: + calRepeats = 0 axis_descriptor = [ { From 8f7ea548c49d2ea9a8ac9e0935460dfc43ecbb75 Mon Sep 17 00:00:00 2001 From: gribeill Date: Wed, 27 Mar 2019 11:05:45 -0400 Subject: [PATCH 163/235] Add stark shift related utility sequences. --- QGL/BasicSequences/StarkShift.py | 168 +++++++++++++++++++++++++++++++ 1 file changed, 168 insertions(+) create mode 100644 QGL/BasicSequences/StarkShift.py diff --git a/QGL/BasicSequences/StarkShift.py b/QGL/BasicSequences/StarkShift.py new file mode 100644 index 00000000..aa9114ae --- /dev/null +++ b/QGL/BasicSequences/StarkShift.py @@ -0,0 +1,168 @@ +from ..PulsePrimitives import * +from ..Compiler import compile_to_hardware +from ..ChannelLibraries import EdgeFactory +from ..PulseSequencePlotter import plot_pulse_files +from .helpers import create_cal_seqs, delay_descriptor, cal_descriptor +import numpy as np +from collections.abc import Iterable + +def StarkShiftSpectroscopy(qubit, measurement, amplitude, + delay=200e-9, length=1e-6, showPlot=False): + """Stark shift spectroscopy experiment. Applies a coherent displacement + to the qubit readout cavity while doing pulsed spectroscopy. + + Args: + qubit: Qubit channel to apply spectroscopy pulse to. + + measurement: Measurement channel to apply displacement pulse to. + + amplitude: Measurement pulse amplitude(s) + + delay: Delay between end of spectroscopy pulse and start of MEAS(qubit). + + length: Total length of cavity displacement pulse. + + Returns: + metafile: Path to compiled sequence metafile. + """ + + if not isinstance(amplitude, Iterable): + amplitude = [amplitude] + + def stark_shift_pulse(amp): + pump_pulse = Utheta(measurement, amp=amp, length=length) + l1 = length - delay - qubit.pulse_params["length"] - delay + spec_pulse = Id(qubit, length=l1)+X(qubit)+Id(qubit,length=delay) + return spec_pulse*pump_pulse + + seqs = [[stark_shift_pulse(a), MEAS(qubit)] for a in amplitude] + axis_descriptor = { + 'name': 'Stark Shift Amplitude', + 'unit': None, + 'points': amplitude, + 'partition': 1 + } + metafile = compile_to_hardware(seqs, 'StarkSpec/StarkSpec', axis_descriptor=axis_descriptor) + + if showPlot: + plot_pulse_files(metafile) + + return metafile + +def StarkEcho(qubit, measurement, amplitudes, delays, + wait=200e-9, periods=4, showPlot=False): + """Hahn echo sequence with a coherent displacement of the qubit measurement cavity. + Used to measure photon-induced dephasing. This sequence can cause a lot of cache pressure + so number of points may be limited. + + Args: + qubit: Qubit channel for Hahn echo. + + measurement: Measurement channel of qubit. + + amplitudes: Amplitude(s) of cavity displacement pulse. + + delays: Hahn echo delays - the t in 90-t-180-t-180. + + wait: Delay between end of cavity displacement pulse and start of MEAS(qubit). + + periods: Number of artificial oscillations. + + Returns: + metafile: Path to compiled sequence metafile. + """ + + if not isinstance(amplitudes, Iterable): + amplitudes = [amplitudes] + + if not isinstance(delays, Iterable): + delays = [delays] + + for k in range(len(pulseSpacings)): + seqs.append([X90(qubit), Id(qubit, pulseSpacings[k]), Y(qubit), Id(qubit,pulseSpacings[k]), \ + U90(qubit,phase=2*pi*periods/len(pulseSpacings)*k), MEAS(qubit)]) + + def echo_phase(n): + return 2*np.pi*periods/len(delays)*n + + def echo_stark(n, amp, max_delay, meas_delay=200e-9): + x_len = qubit.pulse_params["length"] + max_len = 3*x_len + 2*max_delay + meas_delay + echo_wait = total_len - (3*x_len + 2*delays[n]) + + echo_seq = Id(qubit, echo_wait) + X90(qubit) + Id(qubit, delays[n]) + \ + Y(qubit) + Id(qubit, delays[n]) + U90(qubit, echo_phase(n)) + + meas_seq = Utheta(measurement, amp=amp, length=max_len) + + return echo_seq*meas_seq + + + seqs = [[echo_stark(n, amp, np.max(delays)), Id(measurement, length=wait), MEAS(qubit)] + for n, amp in product(range(len(delays)), amplitudes)] + + axis_descriptor = delay_descriptor(delays) * len(amplitudes) + + metafile = compile_to_hardware(seqs, 'StarkEcho/StarkEcho', axis_descriptor=axis_descriptor) + + if showPlot: + plot_pulse_files(metafile) + + return metafile + + +def CavityPumpProbe(qubit, measurement, offsets, amplitude, + length=1e-6, wait=1e-6, showPlot=False): + """Time resolved cavity spectroscopy. Applies a coherent displacement to qubit + readout cavity while sweeping qubit spectroscopy pulse delay. Useful to measure + cavity kappa and cavity population. + + Args: + qubit: Qubit channel for spectroscopy. + + measurement: Measurement channel of qubit. + + offsets: Spectroscopy pulse offset relative to start of cavity displacement pulse. + + amplitude: Measurement pulse amplitude. + + length: Total length of cavity displacement pulse. + + wait: Delay between end of cavity displacement pulse and start of MEAS(qubit). + + Returns: + metafile: Path to compiled sequence metafile. + """ + + if not isinstance(offsets, Iterable): + offsets = [offsets] + + def cavity_probe(offset): + pump_pulse = Utheta(measurement, amp=amp, length=length) + x_len = qubit.pulse_params["length"] + if offset < -1*x_len: + return [X(qubit), Id(qubit, length=(-x_len-offset)), pump_pulse, Id(qubit, length=wait)] + elif offset < 0: + total_len = length-offset + pm = Id(measurement, length=offset)+pump_pulse + pq = X(qubit)+Id(qubit, length=(total_len-x_len)) + return [pm*pq, Id(qubit, length=wait)] + elif offset < length: + pq = Id(qubit, length=offset)+X(qubit)+Id(qubit, length=(length-offset-x_len)) + return [pump_pulse*pq, Id(qubit, length=wait)] + elif offset >= length: + assert offset < (length+wait), f"Wait time {wait} is too short!" + wait_len = wait - (offset-length+x_len) + return [pump_pulse, Id(qubit, length=(offset-length)), X(qubit), Id(qubit, length=wait_len)] + + seqs = [[cavity_probe(off), MEAS(qubit)] for off in offsets] + axis_descriptor = delay_descriptor(offsets) + metafile = compile_to_hardware(seqs, 'CavityPumpProbe/CavityPumpProbe', axis_descriptor=axis_descriptor) + + if showPlot: + plot_pulse_files(metafile) + + return metafile + + + From 6073c5adafdab16cc136398f61e3de9e16708d2f Mon Sep 17 00:00:00 2001 From: gribeill Date: Wed, 27 Mar 2019 12:04:55 -0400 Subject: [PATCH 164/235] Get everything working... various bug fixes and add stark shift functions to QGL global import. --- QGL/BasicSequences/StarkShift.py | 197 +++++++++++++++---------------- QGL/BasicSequences/__init__.py | 1 + 2 files changed, 99 insertions(+), 99 deletions(-) diff --git a/QGL/BasicSequences/StarkShift.py b/QGL/BasicSequences/StarkShift.py index aa9114ae..cdad7ea3 100644 --- a/QGL/BasicSequences/StarkShift.py +++ b/QGL/BasicSequences/StarkShift.py @@ -5,162 +5,161 @@ from .helpers import create_cal_seqs, delay_descriptor, cal_descriptor import numpy as np from collections.abc import Iterable +from itertools import product -def StarkShiftSpectroscopy(qubit, measurement, amplitude, - delay=200e-9, length=1e-6, showPlot=False): - """Stark shift spectroscopy experiment. Applies a coherent displacement - to the qubit readout cavity while doing pulsed spectroscopy. +def StarkSpectroscopy(qubit, measurement, amplitude, + delay=200e-9, length=1e-6, showPlot=False): + """Stark shift spectroscopy experiment. Applies a coherent displacement + to the qubit readout cavity while doing pulsed spectroscopy. - Args: - qubit: Qubit channel to apply spectroscopy pulse to. + Args: + qubit: Qubit channel to apply spectroscopy pulse to. - measurement: Measurement channel to apply displacement pulse to. + measurement: Measurement channel to apply displacement pulse to. - amplitude: Measurement pulse amplitude(s) + amplitude: Measurement pulse amplitude(s) - delay: Delay between end of spectroscopy pulse and start of MEAS(qubit). + delay: Delay between end of spectroscopy pulse and start of MEAS(qubit). - length: Total length of cavity displacement pulse. + length: Total length of cavity displacement pulse. - Returns: - metafile: Path to compiled sequence metafile. - """ + Returns: + metafile: Path to compiled sequence metafile. + """ - if not isinstance(amplitude, Iterable): - amplitude = [amplitude] + if not isinstance(amplitude, Iterable): + amplitude = [amplitude] - def stark_shift_pulse(amp): - pump_pulse = Utheta(measurement, amp=amp, length=length) - l1 = length - delay - qubit.pulse_params["length"] - delay - spec_pulse = Id(qubit, length=l1)+X(qubit)+Id(qubit,length=delay) - return spec_pulse*pump_pulse + def stark_shift_pulse(amp): + pump_pulse = Utheta(measurement, amp=amp, length=length) + l1 = length - delay - qubit.pulse_params["length"] - delay + spec_pulse = Id(qubit, length=l1)+X(qubit)+Id(qubit,length=delay) + return spec_pulse*pump_pulse seqs = [[stark_shift_pulse(a), MEAS(qubit)] for a in amplitude] axis_descriptor = { 'name': 'Stark Shift Amplitude', 'unit': None, - 'points': amplitude, + 'points': list(amplitude), 'partition': 1 } metafile = compile_to_hardware(seqs, 'StarkSpec/StarkSpec', axis_descriptor=axis_descriptor) if showPlot: - plot_pulse_files(metafile) + plot_pulse_files(metafile) return metafile def StarkEcho(qubit, measurement, amplitudes, delays, - wait=200e-9, periods=4, showPlot=False): - """Hahn echo sequence with a coherent displacement of the qubit measurement cavity. - Used to measure photon-induced dephasing. This sequence can cause a lot of cache pressure - so number of points may be limited. + wait=200e-9, periods=4, showPlot=False): + """Hahn echo sequence with a coherent displacement of the qubit measurement cavity. + Used to measure photon-induced dephasing. This sequence can cause a lot of cache pressure + so number of points may be limited. - Args: - qubit: Qubit channel for Hahn echo. + TODO: Use QGL intrinsics to reduce sequence and memory cache utilization. - measurement: Measurement channel of qubit. - - amplitudes: Amplitude(s) of cavity displacement pulse. + Args: + qubit: Qubit channel for Hahn echo. - delays: Hahn echo delays - the t in 90-t-180-t-180. + measurement: Measurement channel of qubit. + + amplitudes: Amplitude(s) of cavity displacement pulse. - wait: Delay between end of cavity displacement pulse and start of MEAS(qubit). + delays: Hahn echo delays - the t in 90-t-180-t-180. - periods: Number of artificial oscillations. + wait: Delay between end of cavity displacement pulse and start of MEAS(qubit). - Returns: - metafile: Path to compiled sequence metafile. - """ + periods: Number of artificial oscillations. - if not isinstance(amplitudes, Iterable): - amplitudes = [amplitudes] + Returns: + metafile: Path to compiled sequence metafile. + """ - if not isinstance(delays, Iterable): - delays = [delays] + if not isinstance(amplitudes, Iterable): + amplitudes = [amplitudes] - for k in range(len(pulseSpacings)): - seqs.append([X90(qubit), Id(qubit, pulseSpacings[k]), Y(qubit), Id(qubit,pulseSpacings[k]), \ - U90(qubit,phase=2*pi*periods/len(pulseSpacings)*k), MEAS(qubit)]) + if not isinstance(delays, Iterable): + delays = [delays] def echo_phase(n): - return 2*np.pi*periods/len(delays)*n + return 2*np.pi*periods/len(delays)*n - def echo_stark(n, amp, max_delay, meas_delay=200e-9): - x_len = qubit.pulse_params["length"] - max_len = 3*x_len + 2*max_delay + meas_delay - echo_wait = total_len - (3*x_len + 2*delays[n]) + def echo_stark(n, amp, max_delay, meas_delay=200e-9): + x_len = qubit.pulse_params["length"] + max_len = 3*x_len + 2*max_delay + meas_delay + echo_wait = max_len - (3*x_len + 2*delays[n]) - echo_seq = Id(qubit, echo_wait) + X90(qubit) + Id(qubit, delays[n]) + \ - Y(qubit) + Id(qubit, delays[n]) + U90(qubit, echo_phase(n)) + echo_seq = Id(qubit, echo_wait) + X90(qubit) + Id(qubit, delays[n]) + \ + Y(qubit) + Id(qubit, delays[n]) + U90(qubit, echo_phase(n)) - meas_seq = Utheta(measurement, amp=amp, length=max_len) + meas_seq = Utheta(measurement, amp=amp, length=max_len) - return echo_seq*meas_seq + return echo_seq*meas_seq seqs = [[echo_stark(n, amp, np.max(delays)), Id(measurement, length=wait), MEAS(qubit)] - for n, amp in product(range(len(delays)), amplitudes)] + for n, amp in product(range(len(delays)), amplitudes)] - axis_descriptor = delay_descriptor(delays) * len(amplitudes) + axis_descriptor = [delay_descriptor(delays)] * len(amplitudes) metafile = compile_to_hardware(seqs, 'StarkEcho/StarkEcho', axis_descriptor=axis_descriptor) if showPlot: - plot_pulse_files(metafile) + plot_pulse_files(metafile) return metafile def CavityPumpProbe(qubit, measurement, offsets, amplitude, - length=1e-6, wait=1e-6, showPlot=False): - """Time resolved cavity spectroscopy. Applies a coherent displacement to qubit - readout cavity while sweeping qubit spectroscopy pulse delay. Useful to measure - cavity kappa and cavity population. - - Args: - qubit: Qubit channel for spectroscopy. - - measurement: Measurement channel of qubit. - - offsets: Spectroscopy pulse offset relative to start of cavity displacement pulse. - - amplitude: Measurement pulse amplitude. - - length: Total length of cavity displacement pulse. - - wait: Delay between end of cavity displacement pulse and start of MEAS(qubit). - - Returns: - metafile: Path to compiled sequence metafile. - """ - - if not isinstance(offsets, Iterable): - offsets = [offsets] - - def cavity_probe(offset): - pump_pulse = Utheta(measurement, amp=amp, length=length) - x_len = qubit.pulse_params["length"] - if offset < -1*x_len: - return [X(qubit), Id(qubit, length=(-x_len-offset)), pump_pulse, Id(qubit, length=wait)] - elif offset < 0: - total_len = length-offset - pm = Id(measurement, length=offset)+pump_pulse - pq = X(qubit)+Id(qubit, length=(total_len-x_len)) - return [pm*pq, Id(qubit, length=wait)] - elif offset < length: - pq = Id(qubit, length=offset)+X(qubit)+Id(qubit, length=(length-offset-x_len)) - return [pump_pulse*pq, Id(qubit, length=wait)] - elif offset >= length: - assert offset < (length+wait), f"Wait time {wait} is too short!" - wait_len = wait - (offset-length+x_len) - return [pump_pulse, Id(qubit, length=(offset-length)), X(qubit), Id(qubit, length=wait_len)] + length=1e-6, wait=1e-6, showPlot=False): + """Time resolved cavity spectroscopy. Applies a coherent displacement to qubit + readout cavity while sweeping qubit spectroscopy pulse delay. Useful to measure + cavity kappa and cavity population. + + Args: + qubit: Qubit channel for spectroscopy. + + measurement: Measurement channel of qubit. + + offsets: Spectroscopy pulse offset relative to start of cavity displacement pulse. + + amplitude: Measurement pulse amplitude. + + length: Total length of cavity displacement pulse. + + wait: Delay between end of cavity displacement pulse and start of MEAS(qubit). + + Returns: + metafile: Path to compiled sequence metafile. + """ + + if not isinstance(offsets, Iterable): + offsets = [offsets] + + def cavity_probe(offset): + pump_pulse = Utheta(measurement, amp=amplitude, length=length) + x_len = qubit.pulse_params["length"] + if offset < -1*x_len: + return [X(qubit), Id(qubit, length=(-x_len-offset)), pump_pulse, Id(qubit, length=wait)] + elif offset < 0: + total_len = length-offset + pm = Id(measurement, length=offset)+pump_pulse + pq = X(qubit)+Id(qubit, length=(total_len-x_len)) + return [pm*pq, Id(qubit, length=wait)] + elif offset < length: + pq = Id(qubit, length=offset)+X(qubit)+Id(qubit, length=(length-offset-x_len)) + return [pump_pulse*pq, Id(qubit, length=wait)] + elif offset >= length: + assert offset < (length+wait), f"Wait time {wait} is too short!" + wait_len = wait - (offset-length+x_len) + return [pump_pulse, Id(qubit, length=(offset-length)), X(qubit), Id(qubit, length=wait_len)] seqs = [[cavity_probe(off), MEAS(qubit)] for off in offsets] axis_descriptor = delay_descriptor(offsets) metafile = compile_to_hardware(seqs, 'CavityPumpProbe/CavityPumpProbe', axis_descriptor=axis_descriptor) if showPlot: - plot_pulse_files(metafile) + plot_pulse_files(metafile) return metafile diff --git a/QGL/BasicSequences/__init__.py b/QGL/BasicSequences/__init__.py index d05862f9..882f430e 100644 --- a/QGL/BasicSequences/__init__.py +++ b/QGL/BasicSequences/__init__.py @@ -8,3 +8,4 @@ from .CR import EchoCRPhase, EchoCRLen, EchoCRAmp, PiRabi from .AllXY import AllXY from .Feedback import Reset, BitFlip3, MajorityVoteN +from .StarkShift import StarkSpectroscopy, StarkEcho, CavityPumpProbe From b1716d5f9d79865619702265587f9e58d913dd7d Mon Sep 17 00:00:00 2001 From: gribeill Date: Wed, 27 Mar 2019 14:07:08 -0400 Subject: [PATCH 165/235] Make sure axis descriptors are lists --- QGL/BasicSequences/StarkShift.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/QGL/BasicSequences/StarkShift.py b/QGL/BasicSequences/StarkShift.py index cdad7ea3..070d5c50 100644 --- a/QGL/BasicSequences/StarkShift.py +++ b/QGL/BasicSequences/StarkShift.py @@ -37,12 +37,12 @@ def stark_shift_pulse(amp): return spec_pulse*pump_pulse seqs = [[stark_shift_pulse(a), MEAS(qubit)] for a in amplitude] - axis_descriptor = { + axis_descriptor = [{ 'name': 'Stark Shift Amplitude', 'unit': None, 'points': list(amplitude), 'partition': 1 - } + }] metafile = compile_to_hardware(seqs, 'StarkSpec/StarkSpec', axis_descriptor=axis_descriptor) if showPlot: @@ -155,7 +155,7 @@ def cavity_probe(offset): return [pump_pulse, Id(qubit, length=(offset-length)), X(qubit), Id(qubit, length=wait_len)] seqs = [[cavity_probe(off), MEAS(qubit)] for off in offsets] - axis_descriptor = delay_descriptor(offsets) + axis_descriptor = [delay_descriptor(offsets)] metafile = compile_to_hardware(seqs, 'CavityPumpProbe/CavityPumpProbe', axis_descriptor=axis_descriptor) if showPlot: From 575975007a58db14cb58bb7b1f378ff926e3fa56 Mon Sep 17 00:00:00 2001 From: "Graham E. Rowlands" Date: Tue, 2 Apr 2019 11:55:44 -0400 Subject: [PATCH 166/235] Fixes for adding stream selectors back in --- QGL/ChannelLibraries.py | 22 +++++++--------------- QGL/config.py | 2 +- 2 files changed, 8 insertions(+), 16 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index fda3363d..70fdefab 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -361,15 +361,11 @@ def new_APS2_rack(self, label, ip_addresses, tdm_ip=None, **kwargs): def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): phys_channels = (1, 2) - dsp_channels = (1, 2) - stream_types = ("raw", "demodulated", "integrated") - chans = [] - for p, d, s in itertools.product(phys_channels, dsp_channels, stream_types): - chans.append(Channels.ReceiverChannel(label=f"RecvChan-{label}-{s}-{p}-{d}", - channel=p, dsp_channel=d, stream_type=s, - channel_db=self.channelDatabase)) + for phys_chan in (1,2): + chans.append(Channels.ReceiverChannel(label=f"RecvChan-{label}-{phys_chan}", + channel=phys_chan, channel_db=self.channelDatabase)) this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=chans, record_length=record_length, channel_db=self.channelDatabase, **kwargs) @@ -377,12 +373,6 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): this_receiver.stream_types = "raw, demodulated, integrated" this_receiver.address = address - # Add a default kernel - for chan in chans: - if chan.stream_type is "integrated": - chan.kernel = np.ones(record_length) - - self.add_and_update_dict(this_receiver) return this_receiver @@ -443,8 +433,10 @@ def set_control(self, qubit_or_edge, transmitter, generator=None): def new_edge(self, source, target): label = f"{source.label}->{target.label}" if label in self.channelDict: - raise ValueError("Cannot construct edge {label} since it is already in the channel library.") - edge = Channels.Edge(label=f"{source.label}->{target.label}", source=source, target=target, channel_db=self.channelDatabase) + edge = self.channelDict[f"{source.label}->{target.label}"] + logger.warning(f"The edge {source.label}->{target.label} already exists: using this edge.") + else: + edge = Channels.Edge(label=f"{source.label}->{target.label}", source=source, target=target, channel_db=self.channelDatabase) self.add_and_update_dict(edge) return edge diff --git a/QGL/config.py b/QGL/config.py index 96950d95..e8ae133d 100644 --- a/QGL/config.py +++ b/QGL/config.py @@ -13,7 +13,7 @@ if os.getenv('AWG_DIR'): AWGDir = os.getenv('AWG_DIR') else: - logger.warning("Defaulting to temporary directory for AWG sequence file outputs.") + logger.warning("AWG_DIR environment variable not defined. Unless otherwise specified, using temporary directory for AWG sequence file outputs.") AWGDir = tempfile.mkdtemp(prefix="AWG") # The db file, where the channel libraries are stored From 37a3d1281d63adbc8f16f03b093c88e772b3362a Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 17 Apr 2019 22:10:32 -0400 Subject: [PATCH 167/235] Now use separate sessions for channel lib and pipeline --- QGL/ChannelLibraries.py | 31 +++++-------------------------- 1 file changed, 5 insertions(+), 26 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 70fdefab..c1b52639 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -50,8 +50,6 @@ from . import PulseShapes from .PulsePrimitives import clear_pulse_cache -from sqlalchemy.orm.session import make_transient -from sqlalchemy.pool import StaticPool from IPython.display import HTML, display channelLib = None @@ -87,35 +85,16 @@ def wrapper(cls, label, *args, **kwargs): class ChannelLibrary(object): - def __init__(self, db_resource_name=None): + def __init__(self, db_resource_name=":memory:", db_provider="sqlite"): """Create the channel library.""" global channelLib - self.db_provider = "sqlite" - self.db_resource_name = ":memory:" - - if bbndb.engine: - # Use current db - self.db = bbndb.engine - else: - - if db_resource_name: - self.db_resource_name = db_resource_name - elif config.load_db(): - self.db_resource_name = config.load_db() - - self.db = bbndb.engine = bbndb.create_engine(f'{self.db_provider}:///{self.db_resource_name}', - connect_args={'check_same_thread':False}, - poolclass=StaticPool, - echo=False) - - bbndb.Base.metadata.create_all(bbndb.engine) - bbndb.Session.configure(bind=bbndb.engine) - self.Session = bbndb.Session - bbndb.session = self.session = self.Session() - + bbndb.initialize_db(f'{db_provider}:///{db_resource_name}') + self.session = bbndb.get_cl_session() self.connectivityG = nx.DiGraph() + self.db_provider = db_provider + self.db_resource_name = db_resource_name # Check to see whether there is already a temp database working_dbs = self.query(Channels.ChannelDatabase, label="working").all() From e547b5e9aeef65e72ea10ea72fef8c4b2cdc7c02 Mon Sep 17 00:00:00 2001 From: Spencer Fallek <38664981+sfallek1@users.noreply.github.com> Date: Mon, 8 Apr 2019 10:18:16 -0400 Subject: [PATCH 168/235] move away from stream_sel_map --- QGL/ChannelLibraries.py | 26 ++++++++++++++------------ QGL/Compiler.py | 9 +++++---- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index c1b52639..dbf44093 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -266,11 +266,11 @@ def build_connectivity_graph(self): @check_for_duplicates def new_APS2(self, label, address, **kwargs): - chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m1 = Channels.PhysicalMarkerChannel(label=f"{label}-m1", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m2 = Channels.PhysicalMarkerChannel(label=f"{label}-m2", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m3 = Channels.PhysicalMarkerChannel(label=f"{label}-m3", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) - m4 = Channels.PhysicalMarkerChannel(label=f"{label}-m4", instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-1", channel=0, instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-m1", channel=0, instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-m2", channel=1, instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-m3", channel=2, instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-m4", channel=3, instrument=label, translator="APS2Pattern", channel_db=self.channelDatabase) this_transmitter = Channels.Transmitter(label=label, model="APS2", address=address, channels=[chan1, m1, m2, m3, m4], channel_db=self.channelDatabase, **kwargs) this_transmitter.trigger_source = "external" @@ -281,12 +281,12 @@ def new_APS2(self, label, address, **kwargs): @check_for_duplicates def new_APS(self, label, address, **kwargs): - chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) - chan2 = Channels.PhysicalQuadratureChannel(label=f"{label}-34", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) - m1 = Channels.PhysicalMarkerChannel(label=f"{label}-1m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) - m2 = Channels.PhysicalMarkerChannel(label=f"{label}-2m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) - m3 = Channels.PhysicalMarkerChannel(label=f"{label}-3m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) - m4 = Channels.PhysicalMarkerChannel(label=f"{label}-4m1", instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + chan1 = Channels.PhysicalQuadratureChannel(label=f"{label}-12", channel = 0, instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + chan2 = Channels.PhysicalQuadratureChannel(label=f"{label}-34", channel = 1, instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m1 = Channels.PhysicalMarkerChannel(label=f"{label}-1m1", channel=0, instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m2 = Channels.PhysicalMarkerChannel(label=f"{label}-2m1", channel=1, instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m3 = Channels.PhysicalMarkerChannel(label=f"{label}-3m1", channel=2, instrument=label, translator="APSPattern", channel_db=self.channelDatabase) + m4 = Channels.PhysicalMarkerChannel(label=f"{label}-4m1", channel=3, instrument=label, translator="APSPattern", channel_db=self.channelDatabase) this_transmitter = Channels.Transmitter(label=label, model="APS", address=address, channels=[chan1, chan2, m1, m2, m3, m4], channel_db=self.channelDatabase) this_transmitter.trigger_source = "external" @@ -346,11 +346,12 @@ def new_X6(self, label, address, dsp_channel=0, record_length=1024, **kwargs): chans.append(Channels.ReceiverChannel(label=f"RecvChan-{label}-{phys_chan}", channel=phys_chan, channel_db=self.channelDatabase)) - this_receiver = Channels.Receiver(label=label, model="X6-1000M", address=address, channels=chans, + this_receiver = Channels.Receiver(label=label, model="X6", address=address, channels=chans, record_length=record_length, channel_db=self.channelDatabase, **kwargs) this_receiver.trigger_source = "external" this_receiver.stream_types = "raw, demodulated, integrated" this_receiver.address = address + this_receiver.stream_sel = "X6StreamSelector" self.add_and_update_dict(this_receiver) return this_receiver @@ -365,6 +366,7 @@ def new_Alazar(self, label, address, record_length=1024, **kwargs): this_receiver.trigger_source = "external" this_receiver.stream_types = "raw" this_receiver.address = address + this_receiver.stream_sel = "AlazarStreamSelector" self.add_and_update_dict(this_receiver) return this_receiver diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 115338de..ba8e561b 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -399,8 +399,8 @@ def compile_to_hardware(seqs, for wire, pulses in physWires.items(): pattern_module = import_module('QGL.drivers.' + wire.translator) if pattern_module.SEQFILE_PER_CHANNEL: - inst_name = pattern_module.get_true_inst_name(wire.label) - chan_name = pattern_module.get_true_chan_name(wire.label) + inst_name = wire.transmitter.label + chan_name = wire.label has_non_id_pulses = any([len([p for p in ps if isinstance(p,Pulse) and p.label!="Id"]) > 0 for ps in pulses]) label_to_inst[wire.label] = inst_name if has_non_id_pulses: @@ -410,7 +410,7 @@ def compile_to_hardware(seqs, old_wire_instrs[wire] = wire.instrument wire.instrument = wire.label wire.label = chan_name - files[inst_name] = {} + #files[inst_name] = {} # construct channel delay map logger.info("Constructing delay map.") @@ -454,11 +454,12 @@ def compile_to_hardware(seqs, new_meta = data['translator'].write_sequence_file(data, fullFileName) if new_meta: awg_metas[awgName] = new_meta + ChannelLibraries.channelLib[awgName].extra_meta = new_meta # Allow for per channel and per AWG seq files if awgName in label_to_inst: if awgName in label_to_chan: - files[label_to_inst[awgName]][label_to_chan[awgName]] = fullFileName + files[label_to_chan[awgName]] = fullFileName else: files[awgName] = fullFileName From b263a05008313a2fa93cd4c3aedc8c7c2ecb8259 Mon Sep 17 00:00:00 2001 From: Spencer Fallek <38664981+sfallek1@users.noreply.github.com> Date: Wed, 24 Apr 2019 14:06:54 -0400 Subject: [PATCH 169/235] fix aps2 rack typo --- QGL/ChannelLibraries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index dbf44093..602adc57 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -323,7 +323,7 @@ def new_attenuator(self,label,address,attenuation=0): @check_for_duplicates def new_APS2_rack(self, label, ip_addresses, tdm_ip=None, **kwargs): transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] - this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", transmitters=transmitters, channel_db=self.channelDatabase, **kwargs) + this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", master=True, address=ip_addresses[0], transmitters=transmitters, channel_db=self.channelDatabase, **kwargs) for t in transmitters: t.transceiver = this_transceiver From a539652a632c986f7d0a801712f79dba6d5c0137 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 24 Apr 2019 17:36:37 -0400 Subject: [PATCH 170/235] Update to latest sqlalchemy syntax --- QGL/Compiler.py | 64 +++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 35a59b49..fec90274 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -432,26 +432,32 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= physWires = map_logical_to_physical(wireSeqs) # Pave the way for composite instruments, not useful yet... - files = {} - label_to_inst = {} - label_to_chan = {} - old_wire_names = {} - old_wire_instrs = {} + extra_info = {} + extra_info["files"] = {} + extra_info["label_to_inst"] = {} + extra_info["label_to_chan"] = {} + extra_info["old_wire_names"] = {} + extra_info["old_wire_instrs"] = {} + + extra_info["num_measurements"] = num_measurements + extra_info["wire_measurements"] = wire_measurements + extra_info["channels"] = channels + for wire, pulses in physWires.items(): pattern_module = import_module('QGL.drivers.' + wire.translator) if pattern_module.SEQFILE_PER_CHANNEL: inst_name = pattern_module.get_true_inst_name(wire.label) chan_name = pattern_module.get_true_chan_name(wire.label) has_non_id_pulses = any([len([p for p in ps if isinstance(p,Pulse) and p.label!="Id"]) > 0 for ps in pulses]) - label_to_inst[wire.label] = inst_name + extra_info["label_to_inst"][wire.label] = inst_name if has_non_id_pulses: - label_to_chan[wire.label] = chan_name + extra_info["label_to_chan"][wire.label] = chan_name # Change the name/inst for uniqueness, but we must restore this later! - old_wire_names[wire] = wire.label - old_wire_instrs[wire] = wire.instrument + extra_info["old_wire_names"][wire] = wire.label + extra_info["old_wire_instrs"][wire] = wire.instrument wire.instrument = wire.label wire.label = chan_name - files[inst_name] = {} + extra_info["files"][inst_name] = {} # construct channel delay map logger.info("Constructing delay map.") @@ -479,7 +485,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= # on whether we have one sequence per channel logger.info("Bundling wires.") awgData = bundle_wires(physWires, wfs) - return awgData + return awgData, extra_info def compile_to_hardware(seqs, fileName, @@ -505,9 +511,15 @@ def compile_to_hardware(seqs, tdm_seq (optional): compile for TDM ''' # save input code to file + save_code(seqs, fileName + suffix) - awgData = compile_to_IR(seqs, add_slave_trigger=add_slave_trigger, tdm_seq=tdm_seq) + if any([p.isRunTime for p in flatten(seqs)]): + random_cliffords = True + else: + random_cliffords = False + + awgData, extra_info = compile_to_IR(seqs, add_slave_trigger=add_slave_trigger, tdm_seq=tdm_seq, random_cliffords=random_cliffords) # convert to hardware formats # files = {} @@ -528,11 +540,11 @@ def compile_to_hardware(seqs, awg_metas[awgName] = new_meta # Allow for per channel and per AWG seq files - if awgName in label_to_inst: - if awgName in label_to_chan: - files[label_to_inst[awgName]][label_to_chan[awgName]] = fullFileName + if awgName in extra_info["label_to_inst"]: + if awgName in extra_info["label_to_chan"]: + files[extra_info["label_to_inst"][awgName]][extra_info["label_to_chan"][awgName]] = fullFileName else: - files[awgName] = fullFileName + extra_info["files"][awgName] = fullFileName del data del awgData[awgName] @@ -562,21 +574,21 @@ def compile_to_hardware(seqs, axis_descriptor = [{ 'name': 'segment', 'unit': None, - 'points': list(range(1, 1 + num_measurements)), + 'points': list(range(1, 1 + extra_info["num_measurements"])), 'partition': 1 }] receiver_measurements = {} - for wire, n in wire_measurements.items(): + for wire, n in extra_info["wire_measurements"].items(): if wire.receiver_chan and n>0: receiver_measurements[wire.receiver_chan.label] = n meta = { 'database_info': db_info, - 'instruments': files, + 'instruments': extra_info["files"], 'num_sequences': len(seqs), - 'num_measurements': num_measurements, + 'num_measurements': extra_info["num_measurements"], 'axis_descriptor': axis_descriptor, - 'qubits': [c.label for c in channels if isinstance(c, Channels.Qubit)], - 'measurements': [c.label for c in channels if isinstance(c, Channels.Measurement)], + 'qubits': [c.label for c in extra_info["channels"] if isinstance(c, Channels.Qubit)], + 'measurements': [c.label for c in extra_info["channels"] if isinstance(c, Channels.Measurement)], 'receivers': receiver_measurements } if extra_meta: @@ -586,10 +598,10 @@ def compile_to_hardware(seqs, json.dump(meta, FID, indent=2, sort_keys=True) # Restore the wire info - for wire in old_wire_names.keys(): - wire.label = old_wire_names[wire] - for wire in old_wire_instrs.keys(): - wire.instrument = old_wire_instrs[wire] + for wire in extra_info["old_wire_names"].keys(): + wire.label = extra_info["old_wire_names"][wire] + for wire in extra_info["old_wire_instrs"].keys(): + wire.instrument = extra_info["old_wire_instrs"][wire] # Return the filenames we wrote return metafilepath From 52a1bfe218ae8469f1e4ffb18c8c28ebb0b53425 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 26 Apr 2019 16:06:16 -0400 Subject: [PATCH 171/235] Add python egg file to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fcfad9a8..7150e5b0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ *.h5 *.py~ *.py# +*.egg-info +QGL.egg-info/ From be39b866158406fa94a706e534c61ce1770bb272 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 26 Apr 2019 16:07:25 -0400 Subject: [PATCH 172/235] Update Inverse Reset instruction sequence. --- QGL/APS2CustomInstructions.py | 7 +++++-- QGL/RandomCliffordTools.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index ae9c4221..9b8268f9 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -69,8 +69,11 @@ def RandomCliffordInverse(target, addr): LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), Call(target, indirect=True)] -def RandomCliffordInverseReset(addr): #for now don't use addr - return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) +def RandomCliffordInverseReset(val=0x0, addr=0xD): #for now don't use addr + return [Invalidate(addr, 0, tdm=False), + WriteAddr(addr, val, tdm=False), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_INVERSE_RESET", addr, 0xA)] def RandomCliffordSeed(seed): return [Invalidate(0xB, 0, tdm=False), diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 9317b3e2..82288031 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -60,7 +60,7 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): return jt_label def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, - inv_addr=0x3, inv_values=[], + inv_addr=0x4, inv_values=[], clifford_options=default_clifford_options): if jt_label is None: From a03994b97c4204c240538a4b1f8ce3aea28f74a5 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 26 Apr 2019 16:08:56 -0400 Subject: [PATCH 173/235] Output label names and address for debug. --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 5ff60e7f..2a223e7e 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1034,7 +1034,7 @@ def resolve_symbols(seq): labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] symbols = {label: idx for idx, label in labeled_entries} - #print(f"Found labels: {symbols}") + logger.info(f"Found labels: {symbols}") for entry in seq: if entry.target is not None and entry.target in symbols.keys(): entry.address = symbols[entry.target] From cc9841995face97da41913e92a42b16867225b09 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Tue, 30 Apr 2019 10:00:49 -0400 Subject: [PATCH 174/235] mapping for APS2Pattern w/ APS2Rack --- QGL/ChannelLibraries.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index 602adc57..f15816eb 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -323,7 +323,7 @@ def new_attenuator(self,label,address,attenuation=0): @check_for_duplicates def new_APS2_rack(self, label, ip_addresses, tdm_ip=None, **kwargs): transmitters = [self.new_APS2(f"{label}_U{n+1}", f"{ip}") for n, ip in enumerate(ip_addresses)] - this_transceiver = Channels.Transceiver(label=label, model="APS2Rack", master=True, address=ip_addresses[0], transmitters=transmitters, channel_db=self.channelDatabase, **kwargs) + this_transceiver = Channels.Transceiver(label=label, model="APS2", master=True, address=ip_addresses[0], transmitters=transmitters, channel_db=self.channelDatabase, **kwargs) for t in transmitters: t.transceiver = this_transceiver From ea13db8c18681b2bec9682fa9551e9308d136af5 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Tue, 30 Apr 2019 10:21:52 -0400 Subject: [PATCH 175/235] bogus readme commit --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index 974fdbdd..545757d1 100644 --- a/README.md +++ b/README.md @@ -50,6 +50,7 @@ going to System -> Advanced Settings -> Environment variables. On Mac/Linux machines add the following line to your .bashrc or .bash_profile: ``` export PYTHONPATH=/path/to/QGL/repo:$PYTHONPATH``` + The QGL config file will be created the first time you run `import QGL` or `from QGL import *`. ## Dependencies From 6f2a8d25875a45e05747837fa1d17d6b63ffa555 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 2 May 2019 09:40:07 -0400 Subject: [PATCH 176/235] Added bqplot graph plot of channel connectivity using show() method --- QGL/ChannelLibraries.py | 80 ++++++++++++++++++++++++++++++++++++++++- 1 file changed, 79 insertions(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index f15816eb..aa3a0df5 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -37,7 +37,8 @@ import datetime import importlib import inspect -from functools import wraps +import operator +from functools import wraps, reduce import itertools import numpy as np import networkx as nx @@ -45,6 +46,10 @@ import bbndb +from bqplot import Figure, LinearScale +from bqplot.marks import Graph, Lines, Label +from ipywidgets import Layout + from . import config from . import Channels from . import PulseShapes @@ -150,6 +155,79 @@ def ent_by_type(self, obj_type, show=False): else: return q + def show(self, qubits=[]): + # nodes = list(dgraph.nodes()) + edges = [] + qub_objs = qubits if not qubits == [] else self.qubits() + for q in qub_objs: + edges.append((q, q.measure_chan)) + edges.append((q.measure_chan, q.measure_chan.phys_chan)) + edges.append((q.measure_chan.phys_chan,q.measure_chan.phys_chan.transmitter)) + edges.append((q, q.phys_chan)) + edges.append((q.phys_chan, q.phys_chan.transmitter)) + + #Generators + if q.measure_chan.phys_chan.generator: + edges.append((q.measure_chan.phys_chan, q.measure_chan.phys_chan.generator)) + if q.phys_chan.generator: + edges.append((q.phys_chan, q.phys_chan.generator)) + + graph = nx.digraph.DiGraph() + graph.add_edges_from(edges) + + indices = {n: i for i, n in enumerate(graph.nodes())} + node_data = [{'label': str(n)} for n in graph.nodes()] + link_data = [{'source': indices[s], 'target': indices[t]} for s, t in graph.edges()] + + qub_objs.sort(key=lambda x: x.label) + qubit_names = [q.label for q in qub_objs] + + loc = {} + def next_level(nodes, iteration=0, offset=0, accum=[]): + if len(accum) == 0: + loc[nodes[0]] = {'x': 0, 'y': 0} + accum = [nodes] + next_gen_nodes = list(reduce(operator.add, [list(graph.successors(n)) for n in nodes])) + l = len(next_gen_nodes) + if l > 0: + for k,n in enumerate(next_gen_nodes): + loc[n] = {'x': k, 'y': -(iteration+1)} + accum.append(next_gen_nodes) + return next_level(next_gen_nodes, iteration=iteration+1, offset=2.5*l, accum=accum) + else: + return accum + + hierarchy = [next_level([q]) for q in qub_objs] + widest = [max([len(row) for row in qh]) for qh in hierarchy] + for i in range(1, len(qub_objs)): + offset = sum(widest[:i]) + loc[qub_objs[i]]['x'] += offset*3 + for n in nx.descendants(graph, qub_objs[i]): + loc[n]['x'] += offset*3 + + x = [loc[n]['x'] for n in graph.nodes()] + y = [loc[n]['y'] for n in graph.nodes()] + xs = LinearScale(min=min(x)-0.5, max=max(x)+0.6) + ys = LinearScale(min=min(y)-0.5, max=max(y)+0.6) + fig_layout = Layout(width='960px', height='500px') + bq_graph = Graph(node_data=node_data, link_data=link_data, x=x, y=y, scales={'x': xs, 'y': ys}, + link_type='line', colors=['orange'] * len(node_data), directed=False) + bgs_lines = [] + middles = [] + for i in range(len(qub_objs)): + if i==0: + start = -0.4 + end = widest[0]-0.6 + elif i == len(qub_objs): + start = sum(widest)-0.4 + end = max(x)+0.4 + else: + start = sum(widest[:i])-0.4 + end = sum(widest[:i+1])-0.6 + + fig = Figure(marks=[bq_graph], layout=fig_layout) + return fig + def receivers(self): return self.ent_by_type(Channels.Receiver) From 1653ebb659ca25e12fc6c8d3363481f597e2b5ab Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 2 May 2019 14:23:50 -0400 Subject: [PATCH 177/235] Add additional info to connectivity plot --- QGL/ChannelLibraries.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index aa3a0df5..dca7c38b 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -172,11 +172,16 @@ def show(self, qubits=[]): if q.phys_chan.generator: edges.append((q.phys_chan, q.phys_chan.generator)) + # Triggers + if q.measure_chan.trig_chan: + edges.append((q.measure_chan, q.measure_chan.trig_chan)) + + graph = nx.digraph.DiGraph() graph.add_edges_from(edges) indices = {n: i for i, n in enumerate(graph.nodes())} - node_data = [{'label': str(n)} for n in graph.nodes()] + node_data = [{'label': str(n).replace('(','\r\n(')} for n in graph.nodes()] link_data = [{'source': indices[s], 'target': indices[t]} for s, t in graph.edges()] qub_objs.sort(key=lambda x: x.label) From 6c1424196e20cc6fef99481356a055882c6e990f Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Thu, 2 May 2019 14:24:04 -0400 Subject: [PATCH 178/235] Add show_frequency_plan method --- QGL/ChannelLibraries.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/QGL/ChannelLibraries.py b/QGL/ChannelLibraries.py index dca7c38b..3b4b818c 100644 --- a/QGL/ChannelLibraries.py +++ b/QGL/ChannelLibraries.py @@ -46,9 +46,9 @@ import bbndb -from bqplot import Figure, LinearScale +from bqplot import Figure, LinearScale, Axis, Lines, Figure from bqplot.marks import Graph, Lines, Label -from ipywidgets import Layout +from ipywidgets import Layout, VBox, HBox from . import config from . import Channels @@ -233,6 +233,35 @@ def next_level(nodes, iteration=0, offset=0, accum=[]): fig = Figure(marks=[bq_graph], layout=fig_layout) return fig + def show_frequency_plan(self): + c_freqs = {} + m_freqs = {} + for qubit in self.qubits(): + c_freqs[qubit.label] = qubit.frequency*1e-9 + if qubit.phys_chan.generator: + c_freqs[qubit.label] += qubit.phys_chan.generator.frequency*1e-9 + + m_freqs[qubit.label] = qubit.measure_chan.frequency*1e-9 + if qubit.measure_chan.phys_chan.generator: + m_freqs[qubit.label] += qubit.measure_chan.phys_chan.generator.frequency*1e-9 + def spike_at(f): + fs = np.linspace(f-0.02,f+0.02,50) + return fs, np.exp(-(fs-f)**2/0.01**2) + figs = [] + for freqs, ss in zip([c_freqs, m_freqs],["Control","Measure"]): + sx = LinearScale() + sy = LinearScale() + ax = Axis(scale=sx, label="Frequency (GHz)") + ay = Axis(scale=sy, orientation='vertical') + lines = [] + for k,f in freqs.items(): + fs, a = spike_at(f) + lines.append(Lines(x=fs, y=a, scales={'x': sx, 'y': sy})) + labels = Label(x=list(freqs.values()), y=[1.1 for f in freqs], text=list(freqs.keys()), align='middle', scales= {'x': sx, 'y': sy}, + default_size=14, font_weight='bolder', colors=['#4f6367']) + figs.append(Figure(marks=lines+[labels], axes=[ax, ay], title=f"{ss} Frequency Plan")) + return HBox(figs) + def receivers(self): return self.ent_by_type(Channels.Receiver) From 175f397589a851a406425d898753abf1c67301b1 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 22 May 2019 21:21:52 -0400 Subject: [PATCH 179/235] Fix channel lib setup for unit tests --- tests/helpers.py | 2 +- tests/test_Compiler.py | 2 ++ tests/test_Sequences.py | 10 +++++----- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/tests/helpers.py b/tests/helpers.py index df558eed..1f663643 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -2,7 +2,7 @@ def setup_test_lib(): cl = ChannelLibrary(db_resource_name=":memory:") - + cl.clear() q1 = cl.new_qubit(label='q1') q2 = cl.new_qubit(label='q2') q3 = cl.new_qubit(label='q3') diff --git a/tests/test_Compiler.py b/tests/test_Compiler.py index 7754b72a..0482b3e2 100644 --- a/tests/test_Compiler.py +++ b/tests/test_Compiler.py @@ -6,7 +6,9 @@ class CompileUtils(unittest.TestCase): def setUp(self): + print("Running setup") self.cl = ChannelLibrary(db_resource_name=":memory:") + self.cl.clear() self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase) self.q1gate = Channels.LogicalMarkerChannel(label='q1-gate', channel_db=self.cl.channelDatabase) self.q1phys = Channels.PhysicalChannel(label='q1-phys', sampling_rate=1.2e9, channel_db=self.cl.channelDatabase) diff --git a/tests/test_Sequences.py b/tests/test_Sequences.py index d9eb1ee8..923b6add 100644 --- a/tests/test_Sequences.py +++ b/tests/test_Sequences.py @@ -454,7 +454,7 @@ def setUp(self): AWGTestHelper.__init__(self, APS2Pattern) for name in ['APS1', 'APS2', 'APS3', 'APS4', 'APS5', 'APS6']: channelName = name + '-1' - channel = PhysicalQuadratureChannel(label=channelName) + channel = PhysicalQuadratureChannel(label=channelName, channel=0) channel.sampling_rate = 1.2e9 channel.instrument = name channel.translator = 'APS2Pattern' @@ -462,7 +462,7 @@ def setUp(self): for m in range(1, 5): channelName = "{0}-m{1}".format(name, m) - channel = PhysicalMarkerChannel(label=channelName) + channel = PhysicalMarkerChannel(label=channelName, channel=m-1) channel.sampling_rate = 1.2e9 channel.instrument = name channel.translator = 'APS2Pattern' @@ -509,9 +509,9 @@ class TestAPS1(unittest.TestCase, AWGTestHelper, TestSequences): def setUp(self): AWGTestHelper.__init__(self, APSPattern) for name in ['APS1', 'APS2', 'APS3']: - for ch in ['12', '34']: + for i, ch in enumerate(['12', '34']): channelName = name + '-' + ch - channel = PhysicalQuadratureChannel(label=channelName) + channel = PhysicalQuadratureChannel(label=channelName, channel=i) channel.sampling_rate = 1.2e9 channel.instrument = name channel.translator = 'APSPattern' @@ -519,7 +519,7 @@ def setUp(self): for m in range(1, 5): channelName = "{0}-{1}m1".format(name, m) - channel = PhysicalMarkerChannel(label=channelName) + channel = PhysicalMarkerChannel(label=channelName, channel=m-1) channel.sampling_rate = 1.2e9 channel.instrument = name channel.translator = 'APSPattern' From 9a3f221b9c450a28ae0c965439a60c13371ad1d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Wed, 10 Apr 2019 15:00:06 -0400 Subject: [PATCH 180/235] addr and mask in Invalidate --- QGL/BasicSequences/Feedback.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/BasicSequences/Feedback.py b/QGL/BasicSequences/Feedback.py index b7833556..999cdf86 100644 --- a/QGL/BasicSequences/Feedback.py +++ b/QGL/BasicSequences/Feedback.py @@ -124,8 +124,8 @@ def BitFlip3(data_qs, ancilla_qs, theta=None, phi=None, nrounds=1, meas_delay=1e raise Exception("Wrong number of qubits") seqs = [ DecodeSetRounds(1,0,nrounds), - Invalidate(addr=10, mask=2*nrounds), - Invalidate(addr=11, mask=0x1)] + Invalidate(10, 2*nrounds), + Invalidate(11, 0x1)] # encode single-qubit state into 3 qubits if theta and phi: From 9a11c807c05636d5f0f1966fc9f7df8c993fa71f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Fri, 12 Apr 2019 10:27:13 -0400 Subject: [PATCH 181/235] Fix mv syntax --- QGL/BasicSequences/Feedback.py | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/QGL/BasicSequences/Feedback.py b/QGL/BasicSequences/Feedback.py index 999cdf86..6efabbfe 100644 --- a/QGL/BasicSequences/Feedback.py +++ b/QGL/BasicSequences/Feedback.py @@ -153,7 +153,7 @@ def BitFlip3(data_qs, ancilla_qs, theta=None, phi=None, nrounds=1, meas_delay=1e if docals: seqs += create_cal_seqs(qubits, calRepeats) - metafile = compile_to_hardware(seqs, 'BitFlip/BitFlip') + metafile = compile_to_hardware(seqs, 'BitFlip/BitFlip', tdm_seq=True) return metafile def MajorityVoteN(qubits, nrounds, prep=[], meas_delay=1e-6, docals=False, calRepeats=2): @@ -172,9 +172,9 @@ def MajorityVoteN(qubits, nrounds, prep=[], meas_delay=1e-6, docals=False, calRe metafile : metafile path """ nqubits = len(qubits) - seqs = [MajorityMask(nrounds*nqubits), - Invalidate(addr=10, mask=nrounds*nqubits), - Invalidate(addr=11, mask=1)] + seqs = [MajorityMask(1, 0, nrounds*nqubits), + Invalidate(10, nrounds*nqubits), + Invalidate(11, 1)] if prep: seqs += [reduce(operator.mul, [X(q) for n,q in enumerate(qubits) if prep[n]])] for n in range(nrounds): @@ -187,5 +187,4 @@ def MajorityVoteN(qubits, nrounds, prep=[], meas_delay=1e-6, docals=False, calRe if docals: seqs += create_cal_seqs(qubits, calRepeats) - metafile = compile_to_hardware(seqs, 'MajorityVote/MajorityVote') - return metafile + metafile = compile_to_hardware(seqs, 'MajorityVote/MajorityVote', tdm_seq=True) From cd73f7bf871cfb02be7c4d19e505ad3ca405163a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Diego=20Rist=C3=A8?= Date: Tue, 16 Apr 2019 18:24:13 -0400 Subject: [PATCH 182/235] Set mask correctly Consistent with rest of notation --- QGL/TdmInstructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index 945743c9..4b057d3a 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -41,13 +41,13 @@ def MajorityVote(in_addr, out_addr, nmeas): # alternatively, append the loadcmpv return [LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 2**nmeas-1, True), CustomInstruction('MAJORITY', in_addr, out_addr)] def MajorityMask(in_addr, out_addr, value): - return [WriteAddrInstruction('INVALIDATE', None, 1, in_addr, 0x0, True), WriteAddrInstruction('WRITEADDR', None, 0, in_addr, value, True), LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 0xffff, True), CustomInstruction('MAJORITYMASK', in_addr, out_addr)] + return [WriteAddrInstruction('INVALIDATE', None, 1, in_addr, 0x0, True), WriteAddrInstruction('WRITEADDR', None, 0, in_addr, 2**value-1, True), LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 0xffff, True), CustomInstruction('MAJORITYMASK', in_addr, out_addr)] def Decode(in_addr, out_addr, nmeas): return [LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 2**nmeas-1, True), CustomInstruction('TSM', in_addr, out_addr)] def DecodeSetRounds(in_addr, out_addr, value): - return [WriteAddrInstruction('INVALIDATE', None, 1, in_addr, 0x0, True), WriteAddrInstruction('WRITEADDR', None, 0, in_addr, value, True), LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 0xffff, True), CustomInstruction('TSM_SET_ROUNDS', in_addr, out_addr)] + return [WriteAddrInstruction('INVALIDATE', None, 1, in_addr, 0x0, True), WriteAddrInstruction('WRITEADDR', None, 0, in_addr, 2**value-1, True), LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 0xffff, True), CustomInstruction('TSM_SET_ROUNDS', in_addr, out_addr)] # TODO: the rest of the CUSTOM instructions From eaaa01ea2e27a88c716399d63c34c083956216ec Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Mon, 3 Jun 2019 16:07:59 -0400 Subject: [PATCH 183/235] Add back WFM plotting and GOTO buttons in pattern plotter --- QGL/drivers/APS2Pattern.py | 95 ++++++++++++++++++++++++++++++++++---- 1 file changed, 86 insertions(+), 9 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 845ac4e2..e47db469 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1464,6 +1464,23 @@ def read_instructions(filename): raw_instrs = raw_instructions(filename) return decompile_instructions(raw_instrs) +def read_waveforms(filename): + with open(filename, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + file_version = struct.unpack(' Date: Wed, 28 Nov 2018 12:52:58 -0500 Subject: [PATCH 184/235] Added support for 4 NCOs --- QGL/drivers/APS2Pattern.py | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index e47db469..597a6b6e 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -47,6 +47,7 @@ MAX_NUM_INSTRUCTIONS = 2**26 MAX_REPEAT_COUNT = 2**16 - 1 MAX_TRIGGER_COUNT = 2**32 - 1 +NUM_NCO = 4 # instruction encodings WFM = 0x0 @@ -580,13 +581,19 @@ def to_instruction(self, write_flag=True, label=None): NCO_SELECT_OP_OFFSET = 40 MODULATION_CLOCK = 300e6 + nco_select_bits = {1 : 0b0001, + 2 : 0b0010, + 3 : 0b0100, + 4 : 0b1000, + 15: 0b1111}[self.nco_select] + op_code_map = {"MODULATE": 0x0, "RESET_PHASE": 0x2, "SET_FREQ": 0x6, "SET_PHASE": 0xa, "UPDATE_FRAME": 0xe} payload = (op_code_map[self.instruction] << MODULATOR_OP_OFFSET) | ( - self.nco_select << NCO_SELECT_OP_OFFSET) + (nco_select_bits) << NCO_SELECT_OP_OFFSET) if self.instruction == "MODULATE": #zero-indexed quad count payload |= np.uint32(self.length / ADDRESS_UNIT - 1) @@ -614,9 +621,9 @@ def inject_modulation_cmds(seqs): for ct,seq in enumerate(seqs): #check whether we have modulation commands freqs = np.unique([entry.frequency for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)]) - if len(freqs) > 2: - raise Exception("Max 2 frequencies on the same channel allowed.") - no_freq_cmds = np.all(np.less(np.abs(freqs), 1e-8)) + if len(freqs) > NUM_NCO: + raise Exception("Max {} frequencies on the same channel allowed.".format(NUM_NCO)) + no_freq_cmds = np.allclose(freqs, 0) phases = [entry.phase for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] no_phase_cmds = np.all(np.less(np.abs(phases), 1e-8)) frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] @@ -1134,12 +1141,13 @@ def start_new_seq(): instructions = np.frombuffer(FID.read(8*inst_len), dtype=np.uint64) wf_lib = {} - for i in range(num_chans): - wf_len = struct.unpack(' Date: Wed, 28 Nov 2018 13:05:55 -0500 Subject: [PATCH 185/235] Reset 4 NCOs --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 597a6b6e..851be455 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -647,7 +647,7 @@ def inject_modulation_cmds(seqs): #heuristic to insert phase reset before trigger if we have modulation commands if isinstance(entry, ControlFlow.Wait): if not ( no_modulation_cmds and (cur_freq == 0) and (cur_phase == 0)): - mod_seq.append(ModulationCommand("RESET_PHASE", 0x3)) + mod_seq.append(ModulationCommand("RESET_PHASE", 0xF)) for nco_ind, freq in enumerate(freqs): mod_seq.append( ModulationCommand("SET_FREQ", nco_ind + 1, frequency = -freq) ) elif isinstance(entry, ControlFlow.Return): From 03dee019e04e75457f5bffea5df3e235a30b6843 Mon Sep 17 00:00:00 2001 From: Daniel Ellard Date: Thu, 17 Jan 2019 16:49:02 -0500 Subject: [PATCH 186/235] if the "kind" is unknown, raise an exception this will usually detect when someone is calling qwait the old way, and raise an error that will require correction --- QGL/ControlFlow.py | 1 + 1 file changed, 1 insertion(+) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index a735f24e..b8d99ecc 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -97,6 +97,7 @@ def qwait(kind="TRIG", addr=None, channels=None): raise Exception('Please specify address') return [WriteAddrInstruction('INVALIDATE', None, 1, addr, 0xffffffff, False), LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, 0xff, False)] + raise Exception('Unknown kind parameter [%s]' % str(kind)) def qsync(channels=None): From 5632b17e5f5d5aa82949466285b99f8bb1591027 Mon Sep 17 00:00:00 2001 From: Daniel Ellard Date: Fri, 18 Jan 2019 13:16:41 -0500 Subject: [PATCH 187/235] clean up exceptions raised by qwait ValueError instead of Exception --- QGL/ControlFlow.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index b8d99ecc..719a2964 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -94,10 +94,10 @@ def qwait(kind="TRIG", addr=None, channels=None): return LoadCmp(channels) elif kind == "RAM": if addr is None: - raise Exception('Please specify address') + raise ValueError('Please specify addr') return [WriteAddrInstruction('INVALIDATE', None, 1, addr, 0xffffffff, False), LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, 0xff, False)] - raise Exception('Unknown kind parameter [%s]' % str(kind)) + raise ValueError('Unknown kind parameter [%s]' % str(kind)) def qsync(channels=None): From f4d415f570f029ce1392489a9b46510f9ae0bef3 Mon Sep 17 00:00:00 2001 From: Daniel Ellard Date: Fri, 18 Jan 2019 13:17:22 -0500 Subject: [PATCH 188/235] tests for new qwait sanity checks --- tests/test_ControlFlow.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/test_ControlFlow.py b/tests/test_ControlFlow.py index a1a9e2a6..f9bbaf41 100644 --- a/tests/test_ControlFlow.py +++ b/tests/test_ControlFlow.py @@ -88,6 +88,23 @@ def test_qwait(self): assert (isinstance(seq1[2][0], TdmInstructions.WriteAddrInstruction)) assert (isinstance(seq1[2][1], TdmInstructions.LoadCmpVramInstruction)) + def test_qwait_err(self): + q1 = self.q1 + with self.assertRaises(ValueError) as exc: + seq1 = [qwait(kind='FOO')] + exc_str = str(exc.exception) + assert (exc_str == 'Unknown kind parameter [FOO]') + + with self.assertRaises(ValueError) as exc: + seq1 = [qwait(kind='RAM')] + exc_str = str(exc.exception) + assert (exc_str == 'Please specify addr') + + # test the legal values + seq1 = [qwait(kind='TRIG')] + seq1 = [qwait(kind='CMP')] + seq1 = [qwait(kind='RAM', addr=0xff)] + def test_compile(self): q1 = self.q1 seq1 = [X90(q1), Y90(q1)] From beeeb6682bc47e8c7e28aaa01823c4c794b9443b Mon Sep 17 00:00:00 2001 From: gribeill Date: Thu, 14 Feb 2019 17:53:02 -0500 Subject: [PATCH 189/235] Initial commit of changes for hardware randomization --- QGL/Compiler.py | 36 +++++++++++++++++++++++++++++++++++- QGL/PatternUtils.py | 5 +++++ QGL/PulsePrimitives.py | 15 +++++++++++++-- QGL/PulseSequencer.py | 9 ++++++--- 4 files changed, 59 insertions(+), 6 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index ba8e561b..a7b05b47 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -30,6 +30,7 @@ from . import Channels from . import ChannelLibraries from . import PulseShapes +from . import PulsePrimitives from .PulsePrimitives import Id, clear_pulse_cache from .PulseSequencer import Pulse, PulseBlock, CompositePulse from . import ControlFlow @@ -221,6 +222,10 @@ def generate_waveforms(physicalWires): for pulse in flatten(wire): if not isinstance(pulse, Pulse): continue + if pulse.isRunTime: + #skip run-time pulses as we need to add the set of all possible pulses to + #the waveform table + continue if pulse.hashshape() not in wfs[ch]: if pulse.isTimeAmp: wfs[ch][pulse.hashshape()] = np.ones(1, dtype=np.complex) @@ -228,6 +233,24 @@ def generate_waveforms(physicalWires): wfs[ch][pulse.hashshape()] = pulse.shape return wfs +def add_runtime_pulse_set(physicalWires, wfs): + + for ch, wire in physicalWires.items(): + rt_pulses = [pulse for pulse in flatten(wire) if pulse.isRunTime] + pulse_type = set([pulse.label for pulse in rt_pulses]) + if len(pulse_type) > 1: + raise Exception("All run-time pulses must have the same label.") + if pulse_type not in ("RandomAC, RandomDiAC"): + raise Exception(f"Unknown run time pulse type {pulse_type}.") + + pulse_fn = getattr(PulsePrimitives, pulse_type.split('Random')[-1], None) + + + + + + + def pulses_to_waveforms(physicalWires): logger.debug("Converting pulses_to_waveforms:") @@ -426,6 +449,10 @@ def compile_to_hardware(seqs, logger.info("Generating waveform library.") wfs = generate_waveforms(physWires) + # If there are run-time pulses add these to the library + if PatternUtils.contains_runtime_pulses(seqs): + wfs = add_runtime_pulse_set(physWires, wfs) + # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") physWires = pulses_to_waveforms(physWires) @@ -713,7 +740,8 @@ class Waveform(object): #Use slots to create attributes to save on memory. __slots__ = ["label", "key", "amp", "length", "phase", "frameChange", - "isTimeAmp", "frequency", "logicalChan", "maddr", "startTime"] + "isTimeAmp", "frequency", "logicalChan", "maddr", "startTime", + "isRunTime"] def __init__(self, pulse=None): if pulse is None: @@ -727,6 +755,7 @@ def __init__(self, pulse=None): self.frequency = 0 self.logicalChan = "" self.maddr = (-1, 0) + self.isRunTime = False else: self.label = pulse.label self.key = pulse.hashshape() @@ -738,11 +767,14 @@ def __init__(self, pulse=None): self.frequency = pulse.frequency self.logicalChan = pulse.channel self.maddr = pulse.maddr + self.isRunTime = pulse.isRunTime def __repr__(self): return self.__str__() def __str__(self): + if self.isRunTime: + return f"Hardware-Generated({self.length})" if self.isTimeAmp: TA = 'HIGH' if self.amp != 0 else 'LOW' return "Waveform-TA(" + TA + ", " + str(self.length) + ")" @@ -751,6 +783,8 @@ def __str__(self): self.key)[:6] + ", " + str(self.length) + ")" def __eq__(self, other): + if self.isRunTime or other.isRunTime: + return False if isinstance(other, self.__class__): #No __dict__ property so we check all properties. return all((getattr(self, attr, None) == getattr(other, attr, None) for attr in self.__slots__)) diff --git a/QGL/PatternUtils.py b/QGL/PatternUtils.py index 9298d45e..0719a3e0 100644 --- a/QGL/PatternUtils.py +++ b/QGL/PatternUtils.py @@ -208,6 +208,11 @@ def add_digitizer_trigger(seqs): trig_chan in seq[ct].pulses.keys()): seq[ct] = align('left', seq[ct], TAPulse("TRIG", trig_chan, trig_chan.pulse_params['length'], 1.0, 0.0, 0.0)) +def contains_runtime_pulses(seqs): + """ + Determines if sequences contain run-time generated pulses. + """ + return any([hasattr(entry, 'isRunTime') and entry.isRunTime for entry in flatten(seqs)]) def contains_measurement(entry): """ diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index bfbf03b2..1ad4fbbe 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -25,12 +25,15 @@ from functools import wraps, reduce -def overrideDefaults(chan, updateParams): +def overrideDefaults(chan, updateParams, ignoredStrParams=['isRunTime', 'maddr', 'moffset']): '''Helper function to update any parameters passed in and fill in the defaults otherwise.''' # The default parameter list depends on the channel type so pull out of channel # Then update passed values paramDict = chan.pulse_params.copy() paramDict.update(updateParams) + for param in ignoredStrParams: + if param in paramDict.keys(): + paramDict.pop(param) return paramDict @@ -113,7 +116,7 @@ def Utheta(qubit, else: # linearly scale based upon the 'pi/2' amplitude amp = (angle / (pi/2)) * qubit.pulse_params['pi2Amp'] - return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency) + return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency, **kwargs) # generic pulses around X, Y, and Z axes @@ -337,6 +340,14 @@ def arb_axis_drag(qubit, return Pulse(kwargs["label"] if "label" in kwargs else "ArbAxis", qubit, params, 1.0, aziAngle, frameChange) +def RandomAC(qubit): + params = overrideDefaults(qubit, {}) + return Pulse("RandomAC", qubit, params, isRunTime=True) + +def RandomDiAC(qubit): + params = overrideDefaults(qubit, {}) + params["length"] *= 2.0 + return Pulse("RandomAC", qubit, params, isRunTime=True) def AC(qubit, cliffNum): """ diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 3b260634..81c64a96 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -33,10 +33,11 @@ class Pulse(namedtuple("Pulse", ["label", "channel", "length", "amp", "phase", "frequency", "frameChange", "shapeParams", "isTimeAmp", "isZero", "ignoredStrParams", - "maddr", "moffset"])): + "maddr", "moffset", "isRunTime"])): __slots__ = () - def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, ignoredStrParams=[], maddr=-1, moffset=0, frequency=None): + def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, + ignoredStrParams=[], maddr=-1, moffset=0, frequency=None, isRunTime=False): if frequency: frequency = frequency elif hasattr(channel, 'frequency'): @@ -48,12 +49,14 @@ def __new__(cls, label, channel, shapeParams, amp=1.0, phase=0, frameChange=0, i if param not in shapeParams.keys(): raise NameError("shapeParams must include {0}".format(param)) isTimeAmp = (shapeParams['shape_fun'] == PulseShapes.constant) + if isRunTime: + isTimeAmp = False isZero = (amp == 0) return super(cls, Pulse).__new__(cls, label, channel, shapeParams['length'], amp, phase, frequency, frameChange, shapeParams, isTimeAmp, isZero, ignoredStrParams, - maddr, moffset) + maddr, moffset, isRunTime) def __str__(self): kwvals = [] From 192789d75ba7378e3f85504faf33842e59758e4b Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 18 Feb 2019 17:12:17 -0500 Subject: [PATCH 190/235] WIP --- QGL.egg-info/PKG-INFO | 10 +++++++ QGL.egg-info/SOURCES.txt | 41 +++++++++++++++++++++++++++++ QGL.egg-info/dependency_links.txt | 1 + QGL.egg-info/requires.txt | 6 +++++ QGL.egg-info/top_level.txt | 1 + QGL/Compiler.py | 43 +++++++++++++++++++++---------- QGL/PulsePrimitives.py | 2 +- 7 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 QGL.egg-info/PKG-INFO create mode 100644 QGL.egg-info/SOURCES.txt create mode 100644 QGL.egg-info/dependency_links.txt create mode 100644 QGL.egg-info/requires.txt create mode 100644 QGL.egg-info/top_level.txt diff --git a/QGL.egg-info/PKG-INFO b/QGL.egg-info/PKG-INFO new file mode 100644 index 00000000..ac9daf85 --- /dev/null +++ b/QGL.egg-info/PKG-INFO @@ -0,0 +1,10 @@ +Metadata-Version: 1.0 +Name: QGL +Version: 2.1 +Summary: UNKNOWN +Home-page: https://github.com/BBN-Q/QGL +Author: UNKNOWN +Author-email: UNKNOWN +License: UNKNOWN +Description: UNKNOWN +Platform: UNKNOWN diff --git a/QGL.egg-info/SOURCES.txt b/QGL.egg-info/SOURCES.txt new file mode 100644 index 00000000..945e4a89 --- /dev/null +++ b/QGL.egg-info/SOURCES.txt @@ -0,0 +1,41 @@ +README.md +QGL/BlockLabel.py +QGL/ChannelLibraries.py +QGL/Channels.py +QGL/Cliffords.py +QGL/Compiler.py +QGL/ControlFlow.py +QGL/GSTTools.py +QGL/PatternUtils.py +QGL/Plotting.py +QGL/PulsePrimitives.py +QGL/PulseSequencePlotter.py +QGL/PulseSequencer.py +QGL/PulseShapes.py +QGL/Scheduler.py +QGL/TdmInstructions.py +QGL/Tomography.py +QGL/__init__.py +QGL/config.py +QGL/config_location.py +QGL/mm.py +QGL.egg-info/PKG-INFO +QGL.egg-info/SOURCES.txt +QGL.egg-info/dependency_links.txt +QGL.egg-info/requires.txt +QGL.egg-info/top_level.txt +QGL/BasicSequences/AllXY.py +QGL/BasicSequences/BlankingSweeps.py +QGL/BasicSequences/CR.py +QGL/BasicSequences/Decoupling.py +QGL/BasicSequences/Feedback.py +QGL/BasicSequences/FlipFlop.py +QGL/BasicSequences/RB.py +QGL/BasicSequences/Rabi.py +QGL/BasicSequences/SPAM.py +QGL/BasicSequences/T1T2.py +QGL/BasicSequences/__init__.py +QGL/BasicSequences/helpers.py +QGL/drivers/APS2Pattern.py +QGL/drivers/APSPattern.py +QGL/drivers/__init__.py \ No newline at end of file diff --git a/QGL.egg-info/dependency_links.txt b/QGL.egg-info/dependency_links.txt new file mode 100644 index 00000000..8b137891 --- /dev/null +++ b/QGL.egg-info/dependency_links.txt @@ -0,0 +1 @@ + diff --git a/QGL.egg-info/requires.txt b/QGL.egg-info/requires.txt new file mode 100644 index 00000000..2aa45f83 --- /dev/null +++ b/QGL.egg-info/requires.txt @@ -0,0 +1,6 @@ +bbndb>=0.1 +numpy>=1.11.1 +scipy>=0.17.1 +networkx>=1.11 +bqplot>=0.11.5 +sqlalchemy>=1.2.17 diff --git a/QGL.egg-info/top_level.txt b/QGL.egg-info/top_level.txt new file mode 100644 index 00000000..0e533603 --- /dev/null +++ b/QGL.egg-info/top_level.txt @@ -0,0 +1 @@ +QGL diff --git a/QGL/Compiler.py b/QGL/Compiler.py index a7b05b47..d6e0f14a 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -234,23 +234,40 @@ def generate_waveforms(physicalWires): return wfs def add_runtime_pulse_set(physicalWires, wfs): - for ch, wire in physicalWires.items(): - rt_pulses = [pulse for pulse in flatten(wire) if pulse.isRunTime] - pulse_type = set([pulse.label for pulse in rt_pulses]) + rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] + if len(rt_pulses) == 0: + continue + pulse_type = list(set([pulse.label for pulse in rt_pulses])) + pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) + if len(pulse_type) > 1: - raise Exception("All run-time pulses must have the same label.") - if pulse_type not in ("RandomAC, RandomDiAC"): + raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") + if len(pulse_logical_chan) > 1: + raise Exception(f"Found more than one channel per run-time pulse set: {pulse_logical_chan}.") + if pulse_type[0] not in ("RandomAC, RandomDiAC"): raise Exception(f"Unknown run time pulse type {pulse_type}.") + pulse_fn = getattr(PulsePrimitives, pulse_type[0].split('Random')[-1], None) + if pulse_fn is None: + raise Exception("Was unable to get pulse type for run-time generated pulses.") + + all_cliffords = [pulse_fn(pulse_logical_chan[0], n) for n in range(24)] + #Some could be CompositePulses so crack into indvidual pulses + all_pulses = [] + for cliff in all_cliffords: + try: + for p in cliff.pulses: + all_pulses.append(p) + except AttributeError: + all_pulses.append(cliff) + for cp in all_pulses: + if cp.hashshape() not in wfs[ch]: #don't duplicate data + if cp.isTimeAmp: + wfs[ch][cp.hashshape()] = np.ones(1, dtype=np.complex) + else: + wfs[ch][cp.hashshape()] = cp.shape - pulse_fn = getattr(PulsePrimitives, pulse_type.split('Random')[-1], None) - - - - - - - + return wfs def pulses_to_waveforms(physicalWires): logger.debug("Converting pulses_to_waveforms:") diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 1ad4fbbe..596c79d2 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -347,7 +347,7 @@ def RandomAC(qubit): def RandomDiAC(qubit): params = overrideDefaults(qubit, {}) params["length"] *= 2.0 - return Pulse("RandomAC", qubit, params, isRunTime=True) + return Pulse("RandomDiAC", qubit, params, isRunTime=True) def AC(qubit, cliffNum): """ From a24b0a19ea4cd870e3bb03b5d55294fba9cde84d Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 22 Feb 2019 17:35:26 -0500 Subject: [PATCH 191/235] WIP --- QGL/Compiler.py | 122 ++++++++++++++++++++++--------------- QGL/ControlFlow.py | 3 +- QGL/PulseSequencer.py | 5 ++ QGL/TdmInstructions.py | 4 +- QGL/drivers/APS2Pattern.py | 28 ++++----- 5 files changed, 95 insertions(+), 67 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index d6e0f14a..10e08258 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -233,34 +233,40 @@ def generate_waveforms(physicalWires): wfs[ch][pulse.hashshape()] = pulse.shape return wfs +def get_clifford_type(wire, allowed_types=("RandomAC")): + rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] + if len(rt_pulses) == 0: + return None + pulse_type = list(set([pulse.label for pulse in rt_pulses])) + pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) + if len(pulse_type) > 1: + raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") + if len(pulse_logical_chan) > 1: + raise Exception(f"Found more than one channel per run-time pulse set: {pulse_logical_chan}.") + if pulse_type[0] not in allowed_types: + raise Exception(f"Unknown run time pulse type {pulse_type}.") + pulse_fn = getattr(PulsePrimitives, pulse_type[0].split('Random')[-1], None) + if pulse_fn is None: + raise Exception("Was unable to get pulse type for run-time generated pulses.") + return lambda x: pulse_fn(pulse_logical_chan[0], x) + + def add_runtime_pulse_set(physicalWires, wfs): for ch, wire in physicalWires.items(): - rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] - if len(rt_pulses) == 0: - continue - pulse_type = list(set([pulse.label for pulse in rt_pulses])) - pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) - - if len(pulse_type) > 1: - raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") - if len(pulse_logical_chan) > 1: - raise Exception(f"Found more than one channel per run-time pulse set: {pulse_logical_chan}.") - if pulse_type[0] not in ("RandomAC, RandomDiAC"): - raise Exception(f"Unknown run time pulse type {pulse_type}.") - pulse_fn = getattr(PulsePrimitives, pulse_type[0].split('Random')[-1], None) + pulse_fn = get_clifford_type(wire) if pulse_fn is None: - raise Exception("Was unable to get pulse type for run-time generated pulses.") - - all_cliffords = [pulse_fn(pulse_logical_chan[0], n) for n in range(24)] - #Some could be CompositePulses so crack into indvidual pulses + continue all_pulses = [] + all_cliffords = [pulse_fn(n) for n in range(24)] + #Some could be CompositePulses so crack into indvidual pulses for cliff in all_cliffords: try: for p in cliff.pulses: all_pulses.append(p) except AttributeError: all_pulses.append(cliff) - for cp in all_pulses: + + for cp in all_pulses: #make all-pulses a set? if cp.hashshape() not in wfs[ch]: #don't duplicate data if cp.isTimeAmp: wfs[ch][cp.hashshape()] = np.ones(1, dtype=np.complex) @@ -269,6 +275,15 @@ def add_runtime_pulse_set(physicalWires, wfs): return wfs +def create_clifford_waveforms(physicalWires): + clifford_sets = {} + for ch, wire in physicalWires.items(): + pulse_fn = get_clifford_type(wire) + if pulse_fn is None: + continue + clifford_sets[ch] = [[Waveform(pulse_fn(n))] for n in range(24)] + return clifford_sets + def pulses_to_waveforms(physicalWires): logger.debug("Converting pulses_to_waveforms:") wireOuts = {ch: [] for ch in physicalWires.keys()} @@ -312,7 +327,7 @@ def setup_awg_channels(physicalChannels): return data -def bundle_wires(physWires, wfs): +def bundle_wires(physWires, wfs, cliff_wires=None): awgData = setup_awg_channels(physWires.keys()) for chan in physWires.keys(): _, awgChan = chan.label.rsplit('-', 1) @@ -320,6 +335,8 @@ def bundle_wires(physWires, wfs): awgChan = 'ch' + awgChan awgData[chan.instrument][awgChan]['linkList'] = physWires[chan] awgData[chan.instrument][awgChan]['wfLib'] = wfs[chan] + if cliff_wires is not None and chan in cliff_wires.keys(): + awgData[chan.instrument][awgChan]['clifford_set'] = cliff_wires[chan] if hasattr(chan, 'correctionT'): awgData[chan.instrument][awgChan]['correctionT'] = chan.correctionT return awgData @@ -340,37 +357,12 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_hardware(seqs, - fileName, - library_version=None, - suffix='', - axis_descriptor=None, - add_slave_trigger=True, - extra_meta=None, - tdm_seq = False): - ''' - Compiles 'seqs' to a hardware description and saves it to 'fileName'. - Other inputs: - library_version (optional): string or ChannelLibrary instance to pack in the - metafile. This will be the version of the library loaded during program - execution. Default None uses the current working version. - suffix (optional): string to append to end of fileName, e.g. with - fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 - axis_descriptor (optional): a list of dictionaries describing the effective - axes of the measurements that the sequence will yield. For instance, - if `seqs` generates a Ramsey experiment, axis_descriptor would describe - the time delays between pulses. - add_slave_trigger (optional): add the slave trigger(s) - tdm_seq (optional): compile for TDM - ''' +def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): + ChannelLibraries.channelLib.update_channelDict() clear_pulse_cache() - logger.debug("Compiling %d sequence(s)", len(seqs)) - # save input code to file - save_code(seqs, fileName + suffix) - # all sequences should start with a WAIT for synchronization for seq in seqs: if not isinstance(seq[0], ControlFlow.Wait): @@ -469,6 +461,9 @@ def compile_to_hardware(seqs, # If there are run-time pulses add these to the library if PatternUtils.contains_runtime_pulses(seqs): wfs = add_runtime_pulse_set(physWires, wfs) + cliff_wires = create_clifford_waveforms(physWires) + else: + cliff_wires = None # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") @@ -477,9 +472,36 @@ def compile_to_hardware(seqs, # bundle wires on instruments, or channels depending # on whether we have one sequence per channel logger.info("Bundling wires.") - awgData = bundle_wires(physWires, wfs) - del wireSeqs - gc.collect() + awgData = bundle_wires(physWires, wfs, cliff_wires=cliff_wires) + return awgData + +def compile_to_hardware(seqs, + fileName, + library_version=None, + suffix='', + axis_descriptor=None, + add_slave_trigger=True, + extra_meta=None, + tdm_seq = False): + ''' + Compiles 'seqs' to a hardware description and saves it to 'fileName'. + Other inputs: + library_version (optional): string or ChannelLibrary instance to pack in the + metafile. This will be the version of the library loaded during program + execution. Default None uses the current working version. + suffix (optional): string to append to end of fileName, e.g. with + fileNames = 'test' and suffix = 'foo' might save to test-APSfoo.h5 + axis_descriptor (optional): a list of dictionaries describing the effective + axes of the measurements that the sequence will yield. For instance, + if `seqs` generates a Ramsey experiment, axis_descriptor would describe + the time delays between pulses. + add_slave_trigger (optional): add the slave trigger(s) + tdm_seq (optional): compile for TDM + ''' + # save input code to file + save_code(seqs, fileName + suffix) + + awgData = compile_to_IR(seqs, add_slave_trigger=add_slave_trigger, tdm_seq=tdm_seq) # convert to hardware formats # files = {} @@ -791,7 +813,7 @@ def __repr__(self): def __str__(self): if self.isRunTime: - return f"Hardware-Generated({self.length})" + return f"Hardware-Generated({self.label}, {self.length})" if self.isTimeAmp: TA = 'HIGH' if self.amp != 0 else 'LOW' return "Waveform-TA(" + TA + ", " + str(self.length) + ")" diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index 719a2964..2aea53ad 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -151,8 +151,9 @@ def __init__(self, target): class Call(ControlInstruction): # target is a BlockLabel - def __init__(self, target): + def __init__(self, target, load_addr=False): super(Call, self).__init__("CALL", target=target) + self.load_addr = load_addr class Return(ControlInstruction): diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 81c64a96..71df08c6 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -262,6 +262,11 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + @property + def isRunTime(self): + #Check if any of the member pulses contain a run-time pulse + return any([hasattr(entry, 'isRunTime') and entry.isRunTime for entry in self.pulses.values()]) + @property def channel(self): return self.pulses.keys() diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index 4b057d3a..fb0069e7 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -109,5 +109,5 @@ def __ne__(self, other): return not self == other -# def LoadCmpVram(addr, mask): -# return LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, mask) +def LoadCmpVram(addr, mask, tdm=True): + return LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, mask, tdm) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 851be455..9a3e2290 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -31,6 +31,8 @@ from QGL import PulseSequencer from QGL.PatternUtils import hash_pulse, flatten from QGL import TdmInstructions +from QGL import APS2CustomInstructions +from QGL.APS2CustomInstructions import APS2_CUSTOM_DECODE # Python 2/3 compatibility: use 'int' that subclasses 'long' from builtins import int @@ -86,20 +88,17 @@ CMPTABLE = {'==': EQUAL, '!=': NOTEQUAL, '>': GREATERTHAN, '<': LESSTHAN} -# custom OP_CODES +# custom TDM OP_CODES TDM_MAJORITY_VOTE = 0 TDM_MAJORITY_VOTE_SET_MASK = 1 TDM_TSM_SET_ROUNDS = 2 TDM_TSM = 3 -APS_CUSTOM_DECODE = ["APS_RAND", "APS_CLIFFORD_RAND","APS_CLIFFORD_SET_SEED" ,"APS_CLIFFORD_SET_OFFSET" -, "APS_CLIFFORD_SET_SPACING"] - TDM_CUSTOM_DECODE = ["TDM_MAJORITY_VOTE", "TDM_MAJORITY_VOTE_SET_MASK", "TDM_TSM_SET_ROUNDS", "TDM_TSM"] # Whether we use PHASE_OFFSET modulation commands or bake it into waveform # Default to false as we usually don't have many variants -USE_PHASE_OFFSET_INSTRUCTION = False +USE_PHASE_OFFSET_INSTRUCTION = True # Whether to save the waveform offsets for partial compilation SAVE_WF_OFFSETS = False @@ -446,8 +445,8 @@ def Goto(addr, label=None): return Command(GOTO, addr, label=label) -def Call(addr, label=None): - return Command(CALL, addr, label=label) +def Call(addr, load_addr=False, label=None): + return Command(CALL, addr, label=label, write=load_addr) def Return(label=None): @@ -513,11 +512,11 @@ def LoadCmpVram(addr, mask, label=None): return Instruction(header, payload, label=label) -def preprocess(seqs, shapeLib): +def preprocess(seqs, shapeLib, clifford_set=False): seqs = PatternUtils.convert_lengths_to_samples( seqs, SAMPLING_RATE, ADDRESS_UNIT, Compiler.Waveform) wfLib = build_waveforms(seqs, shapeLib) - inject_modulation_cmds(seqs) + inject_modulation_cmds(seqs, force_phase_update=clifford_set) return seqs, wfLib @@ -611,7 +610,7 @@ def to_instruction(self, write_flag=True, label=None): instr.writeFlag = write_flag return instr -def inject_modulation_cmds(seqs): +def inject_modulation_cmds(seqs, force_phase_update=False): """ Inject modulation commands from phase, frequency and frameChange of waveforms in an IQ waveform sequence. Assume up to 2 NCOs for now. @@ -629,7 +628,7 @@ def inject_modulation_cmds(seqs): frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] no_frame_cmds = np.all(np.less(np.abs(frame_changes), 1e-8)) no_modulation_cmds = no_freq_cmds and no_phase_cmds and no_frame_cmds - + #import pdb; pdb.set_trace() if no_modulation_cmds: continue @@ -672,10 +671,11 @@ def inject_modulation_cmds(seqs): mod_seq.append( ModulationCommand("MODULATE", nco_select, length = entry.length)) pending_frame_update = False #now apply non-zero frame changes after so it is applied at end + if entry.frameChange != 0: pending_frame_update = True #zero length frame changes (Z pulses) need to be combined with the previous frame change or injected where possible - if entry.length == 0: + if entry.length == 0 and not force_phase_update: #if the last is a frame change then we can add to the frame change if isinstance(mod_seq[-1], ModulationCommand) and mod_seq[-1].instruction == "UPDATE_FRAME": mod_seq[-1].phase += entry.frameChange @@ -808,7 +808,7 @@ def create_seq_instructions(seqs, offsets, label = None): isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction) or - isinstance(entry, TdmInstructions.WriteAddrInstruction) or + isinstance(entry, TdmInstructions.Instruction) or isinstance(entry, TdmInstructions.LoadCmpVramInstruction)): if isinstance(entry, BlockLabel.BlockLabel): # carry label forward to next entry @@ -827,7 +827,7 @@ def create_seq_instructions(seqs, offsets, label = None): elif isinstance(entry, ControlFlow.Goto): instructions.append(Goto(entry.target, label=label)) elif isinstance(entry, ControlFlow.Call): - instructions.append(Call(entry.target, label=label)) + instructions.append(Call(entry.target, load_addr = entry.load_addr, label=label)) elif isinstance(entry, ControlFlow.Repeat): instructions.append(Repeat(entry.target, label=label)) # value argument commands From 180ecb17e2f5f00969dd5fedfa383a48e0b6560e Mon Sep 17 00:00:00 2001 From: gribeill Date: Fri, 22 Feb 2019 17:35:48 -0500 Subject: [PATCH 192/235] More WIP... --- QGL/APS2CustomInstructions.py | 74 +++++++++++++++++++++++++++++++++++ QGL/RandomCliffordTools.py | 51 ++++++++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 QGL/APS2CustomInstructions.py create mode 100644 QGL/RandomCliffordTools.py diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py new file mode 100644 index 00000000..cb082dc0 --- /dev/null +++ b/QGL/APS2CustomInstructions.py @@ -0,0 +1,74 @@ +''' +Copyright 2019 Raytheon BBN Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +# Instructions for the the prototype APS2-TDM hardware. + +from .TdmInstructions import CustomInstruction, WriteAddr, Invalidate, LoadCmpVram +from .ControlFlow import LoadCmp, Call + +##custom APS OP_CODES +#APS_CLIFFORD_SET_SEED: Set 32 bit seed for random number generator +#APS_CLIFFORD_SET_OFFSET: Stores jump table starting offset +#APS_CLIFFORD_SET_SPACING: Stores jump table spacing (integer to shift left), +# including interal address representation +#APS_CLIFFORD_INVERSE_RESET: Reset inverse tracker +#APS_CLIFFORD_RAND: Choose randomly from set of 24 waveforms, +# returns starting address of jump table entry from RAM +#APS_CLIFFORD_INVERSE: Store waveform address of inverse waveform to be jumped to + +APS2_CUSTOM_DECODE = {"APS_RAND": 0, + "APS_CLIFFORD_RAND": 2, + "APS_CLIFFORD_INVERSE": 6, + "APS_CLIFFORD_INVERSE_RESET": 7, + "APS_CLIFFORD_SET_SEED": 3, + "APS_CLIFFORD_SET_OFFSET": 4, + "APS_CLIFFORD_SET_SPACING": 5} + +##Note that the expected call pattern for the randomizer is: + +# APS_CLIFFORD_RAND / APS_CLIFFORD_INVERSE +# LOADCMP +# CALL + +def RandomCliffordSetOffset(addr, offset): + return [Invalidate(addr, 0, tdm=False), + WriteAddr(adrr, offset, tdm=False), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0x3)] + +def RandomCliffordSetSpacing(addr, spacing): + return [Invalidate(addr, 0, tdm=False), + WriteAddr(adrr, offset, tdm=False), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0x3)] + +def RandomClifford(target, addr): + return [Invalidate(addr, 0, tdm=False), + CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + Call(target, load_addr=True)] + +def RandomCliffordInverse(target, addr): + return [Invalidate(addr, 0, tdm=False), + CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + Call(target, load_addr=True)] + +def RandomCliffordInverseReset(addr): #for now don't use addr + return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0x0006) + +def RandomCliffordSeed(seed): + raise NotImplementedError("APS_CLIFFORD_SET_SEED not implemented!") diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py new file mode 100644 index 00000000..6273e063 --- /dev/null +++ b/QGL/RandomCliffordTools.py @@ -0,0 +1,51 @@ +''' +Functions for dealing with random clifford pulse sequences + +Copyright 2019 Raytheon BBN Technologies + +Licensed under the Apache License, Version 2.0 (the "License"); +you may not use this file except in compliance with the License. +You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + +Unless required by applicable law or agreed to in writing, software +distributed under the License is distributed on an "AS IS" BASIS, +WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +See the License for the specific language governing permissions and +limitations under the License. +''' + +from . import config +from . import PatternUtils +from .PatternUtils import flatten, has_gate +from . import Channels +from . import ChannelLibraries +from . import PulseShapes +from . import PulsePrimitives +from .PulsePrimitives import Id, clear_pulse_cache +from .PulseSequencer import Pulse, PulseBlock, CompositePulse +from . import ControlFlow +from . import BlockLabel +from . import TdmInstructions # only for APS2-TDM +from . import APS2CustomInstructions + +def generate_clifford_jump_table(cliff_wires, jt_label = None): + """Generate the jump table that will be used to call into the clifford set""" + + if jt_label is None: + jt_label = BlockLabel.BlockLabel("JT") + if not isinstance(jt_label, BlockLabel.BlockLabel): + raise ValueError("Jump table label must be a BlockLabel.") + + cliff_labels = [f"C{n}" for n in range(24)] + jump_table = [jt_label] + for cl in cliff_labels: + jump_table.append(ControlFlow.Call(cl)) + jump_table.append(ControlFlow.Return()) + + for cliff, cl in zip(cliff_wires, cliff_labels): + cliff.insert(0, BlockLabel.BlockLabel(cl)) + cliff.append(ControlFlow.Return()) + + cliff_wires.insert(0, jump_table) From f6aa67d4fb393b96408b6a8caecd2783df2a8380 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:40:30 -0500 Subject: [PATCH 193/235] Add string representation for custom instructions --- QGL/TdmInstructions.py | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index fb0069e7..76780197 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -36,6 +36,13 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + def __str__(self): + return f"{self.instruction}({hex(self.in_addr)}, {hex(self.out_addr)})" + + def __repr__(self): + return self.__str__() + + def MajorityVote(in_addr, out_addr, nmeas): # alternatively, append the loadcmpvram instruction when compiling (see STOREMEAS) return [LoadCmpVramInstruction('LOADCMPVRAM', 1, in_addr, 2**nmeas-1, True), CustomInstruction('MAJORITY', in_addr, out_addr)] @@ -74,6 +81,12 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + def __str__(self): + return f"{self.instruction}({hex(self.addr)}, {hex(self.value)})" + + def __repr__(self): + return self.__str__() + def WriteAddr(addr, value, channel=None, tdm=True): return WriteAddrInstruction('WRITEADDR', channel, 0, addr, value, tdm) @@ -108,6 +121,12 @@ def __eq__(self, other): def __ne__(self, other): return not self == other + def __str__(self): + return f"{self.instruction}({hex(self.addr)}, {hex(self.mask)})" + + def __repr__(self): + return self.__str__() + def LoadCmpVram(addr, mask, tdm=True): return LoadCmpVramInstruction('LOADCMPVRAM', 1, addr, mask, tdm) From 7409fe56938ce2f88d7f976a74115b54a187ac8c Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:41:16 -0500 Subject: [PATCH 194/235] Set write flag to CALL to indicate load from VRAM --- QGL/ControlFlow.py | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index 2aea53ad..daf12587 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -155,6 +155,13 @@ def __init__(self, target, load_addr=False): super(Call, self).__init__("CALL", target=target) self.load_addr = load_addr + def __str__(self): + if self.load_addr: + return f"{self.instruction}({str(self.target)})" + else: + return f"{self.instruction}({str(self.target)}, LOAD)" + + class Return(ControlInstruction): def __init__(self): From b6cc8247448fdcfdd15cdb6e17a5459bec731c99 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:41:42 -0500 Subject: [PATCH 195/235] Update APS2 custom instructions for randomizer --- QGL/APS2CustomInstructions.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index cb082dc0..285f8685 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -63,12 +63,12 @@ def RandomClifford(target, addr): def RandomCliffordInverse(target, addr): return [Invalidate(addr, 0, tdm=False), - CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), + CustomInstruction("APS_CLIFFORD_INVERSE", 0x0, addr), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), Call(target, load_addr=True)] def RandomCliffordInverseReset(addr): #for now don't use addr - return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0x0006) + return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) def RandomCliffordSeed(seed): raise NotImplementedError("APS_CLIFFORD_SET_SEED not implemented!") From fa7aff84df1ec2310d77c00b9e978910ed144e47 Mon Sep 17 00:00:00 2001 From: gribeill Date: Sat, 23 Feb 2019 18:42:21 -0500 Subject: [PATCH 196/235] Tools for random clifford sequences. --- QGL/RandomCliffordTools.py | 46 +++++++++++++++++++++++++++++++++++++- 1 file changed, 45 insertions(+), 1 deletion(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 6273e063..d7584c34 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -17,18 +17,23 @@ ''' from . import config +from functools import reduce from . import PatternUtils from .PatternUtils import flatten, has_gate from . import Channels from . import ChannelLibraries from . import PulseShapes from . import PulsePrimitives +from . import Compiler from .PulsePrimitives import Id, clear_pulse_cache from .PulseSequencer import Pulse, PulseBlock, CompositePulse from . import ControlFlow from . import BlockLabel from . import TdmInstructions # only for APS2-TDM -from . import APS2CustomInstructions +from .APS2CustomInstructions import * +from .Cliffords import C1, inverse_clifford, clifford_multiply + +VALID_CLIFFORD_TYPES = ('RandomAC',) def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" @@ -49,3 +54,42 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): cliff.append(ControlFlow.Return()) cliff_wires.insert(0, jump_table) + +def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, + inv_addr=0x3, inv_values=[]): + + if jt_label is None: + jt_label = BlockLabel.BlockLabel("JT") + if not isinstance(jt_label, BlockLabel.BlockLabel): + raise ValueError("Jump table label must be a BlockLabel.") + + for idx, seq in enumerate(seqs[:]): + if not PatternUtils.contains_runtime_pulses(seq): + continue + new_seq = [] + for pulse in seq: + if isinstance(pulse, Compiler.Waveform) and pulse.isRunTime \ + and pulse.label in VALID_CLIFFORD_TYPES: + has_random_cliff = True + new_seq.extend(RandomClifford(jt_label, cliff_addr)) + print("Inserting clifford pulse!") + else: + new_seq.append(pulse) + + if add_inv: + #insert reset after first wait + w_idx = next(i for i, v in enumerate(new_seq) if isinstance(v, ControlFlow.Wait)) + new_seq.insert(w_idx+1, RandomCliffordInverseReset(0x0)) + #insert at end of sequence or before last GOTO + if isinstance(new_seq[-1], ControlFlow.Goto): + new_seq[-1:-1] = RandomCliffordInverse(jt_label, inv_addr) + else: + new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) + + seqs[idx] = new_seq + +def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): + + c_seqs = [[clifford_type(qubit) for _ in seq] for seq in seqs] + inverses = [inverse_clifford(C1[reduce(clifford_multiply, seq)]) for seq in seqs] + return c_seqs, inverses From fca2e8132c17694bfdb0d45feac1eeb60f8f5e0b Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 25 Feb 2019 17:35:52 -0500 Subject: [PATCH 197/235] Even more WIP --- QGL/APS2CustomInstructions.py | 4 ++- QGL/Compiler.py | 40 ++++++++++++++--------- QGL/PulsePrimitives.py | 2 +- QGL/RandomCliffordTools.py | 4 +-- QGL/TdmInstructions.py | 7 ++-- QGL/drivers/APS2Pattern.py | 61 ++++++++++++++++++++++------------- 6 files changed, 74 insertions(+), 44 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 285f8685..6eb22054 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -29,7 +29,7 @@ # returns starting address of jump table entry from RAM #APS_CLIFFORD_INVERSE: Store waveform address of inverse waveform to be jumped to -APS2_CUSTOM_DECODE = {"APS_RAND": 0, +APS2_CUSTOM = {"APS_RAND": 0, "APS_CLIFFORD_RAND": 2, "APS_CLIFFORD_INVERSE": 6, "APS_CLIFFORD_INVERSE_RESET": 7, @@ -37,6 +37,8 @@ "APS_CLIFFORD_SET_OFFSET": 4, "APS_CLIFFORD_SET_SPACING": 5} +APS2_CUSTOM_DECODE = {v: k for k, v in APS2_CUSTOM.items()} + ##Note that the expected call pattern for the randomizer is: # APS_CLIFFORD_RAND / APS_CLIFFORD_INVERSE diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 10e08258..5c7de855 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -36,6 +36,8 @@ from . import ControlFlow from . import BlockLabel from . import TdmInstructions # only for APS2-TDM +from . import APS2CustomInstructions +from . import RandomCliffordTools import gc logger = logging.getLogger(__name__) @@ -327,7 +329,7 @@ def setup_awg_channels(physicalChannels): return data -def bundle_wires(physWires, wfs, cliff_wires=None): +def bundle_wires(physWires, wfs): awgData = setup_awg_channels(physWires.keys()) for chan in physWires.keys(): _, awgChan = chan.label.rsplit('-', 1) @@ -335,8 +337,6 @@ def bundle_wires(physWires, wfs, cliff_wires=None): awgChan = 'ch' + awgChan awgData[chan.instrument][awgChan]['linkList'] = physWires[chan] awgData[chan.instrument][awgChan]['wfLib'] = wfs[chan] - if cliff_wires is not None and chan in cliff_wires.keys(): - awgData[chan.instrument][awgChan]['clifford_set'] = cliff_wires[chan] if hasattr(chan, 'correctionT'): awgData[chan.instrument][awgChan]['correctionT'] = chan.correctionT return awgData @@ -357,7 +357,7 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): +def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords=False): ChannelLibraries.channelLib.update_channelDict() clear_pulse_cache() @@ -394,7 +394,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): # Compile all the pulses/pulseblocks to sequences of pulses and control flow logger.info("Compiling sequences.") - wireSeqs = compile_sequences(seqs, channels) + wireSeqs = compile_sequences(seqs, channels, random_cliffords=random_cliffords) if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") @@ -459,11 +459,8 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): wfs = generate_waveforms(physWires) # If there are run-time pulses add these to the library - if PatternUtils.contains_runtime_pulses(seqs): + if random_cliffords: wfs = add_runtime_pulse_set(physWires, wfs) - cliff_wires = create_clifford_waveforms(physWires) - else: - cliff_wires = None # replace Pulse objects with Waveforms logger.info("Replacing pulses with waveforms") @@ -472,7 +469,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False): # bundle wires on instruments, or channels depending # on whether we have one sequence per channel logger.info("Bundling wires.") - awgData = bundle_wires(physWires, wfs, cliff_wires=cliff_wires) + awgData = bundle_wires(physWires, wfs) return awgData def compile_to_hardware(seqs, @@ -590,7 +587,7 @@ def compile_to_hardware(seqs, return metafilepath -def compile_sequences(seqs, channels=set()): +def compile_sequences(seqs, channels=set(), random_cliffords=False): ''' Main function to convert sequences to miniLL's and waveform libraries. ''' @@ -604,6 +601,16 @@ def compile_sequences(seqs, channels=set()): subroutines = collect_specializations(seqs) seqs += subroutines + if random_cliffords: + qubits = [q for q in list(channels) if isinstance(q, Channels.Qubit)] + if len(qubits) > 1: + raise Exception("Too many qubits! Don't know how to deal with this yet.") + #replace cliffords with + cliffords = [[get_clifford_type(seqs)(n)] for n in range(24)] + RandomCliffordTools.generate_clifford_jump_table(cliffords) + RandomCliffordTools.insert_clifford_calls(seqs) + seqs += cliffords + #expand the channel definitions for anything defined in subroutines for func in subroutines: channels |= find_unique_channels(subroutines) @@ -665,10 +672,10 @@ def flatten_to_pulses(obj): wires[chan] += [copy(block)] continue # control flow broadcasts to all channels if channel attribute is None - if (isinstance(block, ControlFlow.ControlInstruction) or - isinstance(block, TdmInstructions.WriteAddrInstruction) or - isinstance(block, TdmInstructions.CustomInstruction) or - isinstance(block, TdmInstructions.LoadCmpVramInstruction)): + if isinstance(block, ControlFlow.ControlInstruction): #or + #isinstance(block, TdmInstructions.WriteAddrInstruction) or + #isinstance(block, TdmInstructions.CustomInstruction) or + #isinstance(block, TdmInstructions.LoadCmpVramInstruction)): # Need to deal with attaching measurements and edges to control # instruction. Until we have a proper solution for that, we will # always broadcast control instructions to all channels @@ -677,6 +684,9 @@ def flatten_to_pulses(obj): for chan in block_channels: wires[chan] += [copy(block)] continue + #Don't propagate VRAM instructions + if isinstance(block, TdmInstructions.VRAMInstruction) or isinstance(block, TdmInstructions.CustomInstruction): + continue # propagate frame change from nodes to edges for chan in channels: if block.pulses[chan].frameChange == 0: diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 596c79d2..a648e044 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -372,7 +372,7 @@ def AC(qubit, cliffNum): #Now a big else if chain for to get the specific Clifford if cliffNum == 0: #Identity gate - return Id(qubit, length=0) + return Id(qubit)#Id(qubit, length=0) elif cliffNum == 1: #X90 return X90(qubit) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index d7584c34..d4a0d26e 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -68,11 +68,11 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, continue new_seq = [] for pulse in seq: - if isinstance(pulse, Compiler.Waveform) and pulse.isRunTime \ + if isinstance(pulse, Pulse) and pulse.isRunTime \ and pulse.label in VALID_CLIFFORD_TYPES: has_random_cliff = True new_seq.extend(RandomClifford(jt_label, cliff_addr)) - print("Inserting clifford pulse!") + #print("Inserting clifford pulse!") else: new_seq.append(pulse) diff --git a/QGL/TdmInstructions.py b/QGL/TdmInstructions.py index 76780197..cbb919b5 100644 --- a/QGL/TdmInstructions.py +++ b/QGL/TdmInstructions.py @@ -58,7 +58,10 @@ def DecodeSetRounds(in_addr, out_addr, value): # TODO: the rest of the CUSTOM instructions -class WriteAddrInstruction(object): +class VRAMInstruction(object): + pass + +class WriteAddrInstruction(VRAMInstruction): def __init__(self, name, channel, modifier, addr, value, tdm, **kwargs): self.instruction = name @@ -99,7 +102,7 @@ def CrossBar(addr, value, channel=None, tdm=True): # should this be a high-level def StoreMeas(addr, value, channel=None, tdm=True): return WriteAddrInstruction('STOREMEAS', channel, 5, addr, value, tdm) -class LoadCmpVramInstruction(object): +class LoadCmpVramInstruction(VRAMInstruction): def __init__(self, name, use_vram, addr, mask, tdm): # TODO: sanity checks on input values diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 9a3e2290..bcf695bc 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -32,7 +32,7 @@ from QGL.PatternUtils import hash_pulse, flatten from QGL import TdmInstructions from QGL import APS2CustomInstructions -from QGL.APS2CustomInstructions import APS2_CUSTOM_DECODE +from QGL.APS2CustomInstructions import APS2_CUSTOM, APS2_CUSTOM_DECODE # Python 2/3 compatibility: use 'int' that subclasses 'long' from builtins import int @@ -307,7 +307,10 @@ def __str__(self): store_addr = self.payload & 0xFFFF load_addr = (self.payload >> 16) & 0xFFFF instruction = (self.payload >> 32) & 0xFF - instructionAPS = TDM_CUSTOM_DECODE[instruction] + if self.decode_as_tdm: + instructionAPS = TDM_CUSTOM_DECODE[instruction] + else: + instructionAPS = APS2_CUSTOM_DECODE[instruction] out += " | instruction={0} ({1}), load_addr=0x{2:0x}, store_addr=0x{3:0x}".format(instruction, instructionAPS, load_addr, store_addr) elif instrOpCode == WRITEADDR: @@ -511,12 +514,11 @@ def LoadCmpVram(addr, mask, label=None): payload = (1 << 48) | (mask << 16) | addr return Instruction(header, payload, label=label) - -def preprocess(seqs, shapeLib, clifford_set=False): +def preprocess(seqs, shapeLib): seqs = PatternUtils.convert_lengths_to_samples( seqs, SAMPLING_RATE, ADDRESS_UNIT, Compiler.Waveform) wfLib = build_waveforms(seqs, shapeLib) - inject_modulation_cmds(seqs, force_phase_update=clifford_set) + inject_modulation_cmds(seqs) return seqs, wfLib @@ -722,7 +724,7 @@ def synchronize_clocks(seqs): syncInstructions = [list(filter( lambda s: isinstance(s, ControlFlow.ControlInstruction), seq)) for seq in seqs if seq] - + #import pdb; pdb.set_trace() # Add length to control-flow instructions to make accumulated time match at end of CFI. # Keep running tally of how much each channel has been shifted so far. localShift = [0 for _ in syncInstructions] @@ -808,7 +810,7 @@ def create_seq_instructions(seqs, offsets, label = None): isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction) or - isinstance(entry, TdmInstructions.Instruction) or + #isinstance(entry, TdmInstructions.Instruction) or ?????? isinstance(entry, TdmInstructions.LoadCmpVramInstruction)): if isinstance(entry, BlockLabel.BlockLabel): # carry label forward to next entry @@ -842,11 +844,15 @@ def create_seq_instructions(seqs, offsets, label = None): instructions.append(LoadCmpVram(entry.addr, entry.mask, label=label)) # some TDM instructions are ignored by the APS elif isinstance(entry, TdmInstructions.CustomInstruction): - pass + try: + instructions.append(Custom(entry.in_addr, entry.out_addr, + APS2_CUSTOM[entry.instruction], label=label)) + except KeyError as e: + raise Exception(f"Got unknown APS2 instruction: {e.args[0]} in {str(entry)}.") elif isinstance(entry, TdmInstructions.WriteAddrInstruction): if entry.instruction == 'INVALIDATE' and entry.tdm == False: instructions.append(Invalidate(entry.addr, entry.value, label=label)) - + label=None #Reset label to prevent it propagating in a jump table continue if seq_idx == 0: @@ -990,20 +996,29 @@ def create_instr_data(seqs, offsets, cache_lines): def resolve_symbols(seq): - symbols = {} - # create symbol look-up table - for ct, entry in enumerate(seq): - if entry.label and entry.label not in symbols: - symbols[entry.label] = ct - # then update - for (ct, entry) in enumerate(seq): - if entry.target: - # find next available label. The TDM may miss some labels if branches only contain waveforms (which are ignored) - for k in range(len(seq)-ct): - temp = seq[ct+k] - if temp.target in symbols: - break - entry.address = symbols[temp.target] + + labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] + import pdb; pdb.set_trace() + symbols = {label: idx for idx, label in labeled_entries} + print(f"Found labels: {symbols}") + for entry in seq: + if entry.target is not None and entry.target in symbols.keys(): + entry.address = symbols[entry.target] + + # symbols = {} + # # create symbol look-up table + # for ct, entry in enumerate(seq): + # if entry.label and entry.label not in symbols: + # symbols[entry.label] = ct + # # then update + # for (ct, entry) in enumerate(seq): + # if entry.target: + # # find next available label. The TDM may miss some labels if branches only contain waveforms (which are ignored) + # for k in range(len(seq)-ct): + # temp = seq[ct+k] + # if temp.label in symbols: + # break + # entry.address = symbols[temp.label] def compress_marker(markerLL): From 5fe0c19e6f555fd5aaf3d52a97f49486b512f9ea Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 25 Feb 2019 17:56:00 -0500 Subject: [PATCH 198/235] Remove debug. --- QGL/drivers/APS2Pattern.py | 1 - 1 file changed, 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index bcf695bc..b7acdb0f 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -998,7 +998,6 @@ def create_instr_data(seqs, offsets, cache_lines): def resolve_symbols(seq): labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] - import pdb; pdb.set_trace() symbols = {label: idx for idx, label in labeled_entries} print(f"Found labels: {symbols}") for entry in seq: From edb89fca03bd5d0b5a0460cd66679507b841fad2 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 25 Feb 2019 21:33:07 -0500 Subject: [PATCH 199/235] Make sure Custom instructions not dropped by pattern generator! --- QGL/APS2CustomInstructions.py | 4 ++-- QGL/Compiler.py | 22 ++++++++++++++-------- QGL/PulseSequencer.py | 4 ++++ QGL/RandomCliffordTools.py | 14 +++++++++++++- QGL/drivers/APS2Pattern.py | 6 +++--- 5 files changed, 36 insertions(+), 14 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 6eb22054..3b269884 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -47,13 +47,13 @@ def RandomCliffordSetOffset(addr, offset): return [Invalidate(addr, 0, tdm=False), - WriteAddr(adrr, offset, tdm=False), + WriteAddr(addr, offset, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0x3)] def RandomCliffordSetSpacing(addr, spacing): return [Invalidate(addr, 0, tdm=False), - WriteAddr(adrr, offset, tdm=False), + WriteAddr(addr, spacing, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0x3)] diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 5c7de855..930774d8 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -236,7 +236,15 @@ def generate_waveforms(physicalWires): return wfs def get_clifford_type(wire, allowed_types=("RandomAC")): - rt_pulses = [pulse for pulse in flatten(wire) if isinstance(pulse, Pulse) and pulse.isRunTime] + rt_pulses = [] + for pulse in flatten(wire): + if isinstance(pulse, Pulse) and pulse.isRunTime: + rt_pulses.append(pulse) + elif isinstance(pulse, PulseBlock) and pulse.isRunTime: + for p in pulse.pulses.values(): + if p.isRunTime: + rt_pulses.append(p) + if len(rt_pulses) == 0: return None pulse_type = list(set([pulse.label for pulse in rt_pulses])) @@ -399,7 +407,6 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") return - logger.debug('') logger.debug("Now after gating constraints:") # apply gating constraints @@ -672,10 +679,9 @@ def flatten_to_pulses(obj): wires[chan] += [copy(block)] continue # control flow broadcasts to all channels if channel attribute is None - if isinstance(block, ControlFlow.ControlInstruction): #or - #isinstance(block, TdmInstructions.WriteAddrInstruction) or - #isinstance(block, TdmInstructions.CustomInstruction) or - #isinstance(block, TdmInstructions.LoadCmpVramInstruction)): + if (isinstance(block, ControlFlow.ControlInstruction) or + isinstance(block, TdmInstructions.VRAMInstruction) or + isinstance(block, TdmInstructions.CustomInstruction)): # Need to deal with attaching measurements and edges to control # instruction. Until we have a proper solution for that, we will # always broadcast control instructions to all channels @@ -685,8 +691,8 @@ def flatten_to_pulses(obj): wires[chan] += [copy(block)] continue #Don't propagate VRAM instructions - if isinstance(block, TdmInstructions.VRAMInstruction) or isinstance(block, TdmInstructions.CustomInstruction): - continue + #if isinstance(block, TdmInstructions.VRAMInstruction) or isinstance(block, TdmInstructions.CustomInstruction): + # continue # propagate frame change from nodes to edges for chan in channels: if block.pulses[chan].frameChange == 0: diff --git a/QGL/PulseSequencer.py b/QGL/PulseSequencer.py index 71df08c6..e25abac8 100644 --- a/QGL/PulseSequencer.py +++ b/QGL/PulseSequencer.py @@ -198,6 +198,10 @@ def frameChange(self): def isZero(self): return all(p.isZero for p in self.pulses) + @property + def isRunTime(self): + return any(hasattr(entry, 'isRunTime') and entry.isRunTime for entry in self.pulses) + class PulseBlock(object): ''' diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index d4a0d26e..6863f0b4 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -66,16 +66,27 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, for idx, seq in enumerate(seqs[:]): if not PatternUtils.contains_runtime_pulses(seq): continue + new_seq = [] + for pulse in seq: if isinstance(pulse, Pulse) and pulse.isRunTime \ and pulse.label in VALID_CLIFFORD_TYPES: has_random_cliff = True new_seq.extend(RandomClifford(jt_label, cliff_addr)) - #print("Inserting clifford pulse!") + print("Inserting clifford pulse!") else: new_seq.append(pulse) + info_seqs = [] + info_seqs.extend(RandomCliffordSetOffset(0x1, 0x0)) + info_seqs.extend(RandomCliffordSetSpacing(0x2, 0x1)) + + if isinstance(seq[0], BlockLabel.BlockLabel): + new_seq[1:1] = info_seqs + else: + new_seq[0:0] = info_seqs + if add_inv: #insert reset after first wait w_idx = next(i for i, v in enumerate(new_seq) if isinstance(v, ControlFlow.Wait)) @@ -88,6 +99,7 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, seqs[idx] = new_seq + def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): c_seqs = [[clifford_type(qubit) for _ in seq] for seq in seqs] diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index b7acdb0f..fe2f1d1f 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -641,10 +641,10 @@ def inject_modulation_cmds(seqs, force_phase_update=False): #copies to avoid same object having different timestamps later #copy through BlockLabel - if isinstance(entry, BlockLabel.BlockLabel): + if isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction): mod_seq.append(copy(entry)) #mostly copy through control-flow - elif isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, TdmInstructions.LoadCmpVramInstruction) or isinstance(entry, TdmInstructions.WriteAddrInstruction): + elif isinstance(entry, ControlFlow.ControlInstruction) or isinstance(entry, TdmInstructions.VRAMInstruction): #heuristic to insert phase reset before trigger if we have modulation commands if isinstance(entry, ControlFlow.Wait): if not ( no_modulation_cmds and (cur_freq == 0) and (cur_phase == 0)): @@ -804,7 +804,6 @@ def create_seq_instructions(seqs, offsets, label = None): write_flags = [True] * len(entries) for ct, (entry, seq_idx) in enumerate(entries): - #use first non empty sequence for control flow if seq_idx == first_non_empty and ( isinstance(entry, ControlFlow.ControlInstruction) or @@ -844,6 +843,7 @@ def create_seq_instructions(seqs, offsets, label = None): instructions.append(LoadCmpVram(entry.addr, entry.mask, label=label)) # some TDM instructions are ignored by the APS elif isinstance(entry, TdmInstructions.CustomInstruction): + print(str(entry)) try: instructions.append(Custom(entry.in_addr, entry.out_addr, APS2_CUSTOM[entry.instruction], label=label)) From 41e488e09ba732fe237c57ad9817d31cb2fba854 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 10:50:59 -0500 Subject: [PATCH 200/235] Add color for custom instructions --- QGL/drivers/APS2Pattern.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index fe2f1d1f..1e564978 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1542,7 +1542,8 @@ def display_raw_file(filename): colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), - "MARKER": QColor(150,150,200)} + "MARKER": QColor(150,150,200), + "CUSTOM": QColor(200,65,200)} class MatplotlibWidget(QWidget): def __init__(self, I, Q, parent=None): From 6f6530af1123c88020e0e0b0b654deeb50f166ee Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 11:10:54 -0500 Subject: [PATCH 201/235] Don't drop WRITEADDR instructions! --- QGL/drivers/APS2Pattern.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 1e564978..158abedf 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -810,7 +810,7 @@ def create_seq_instructions(seqs, offsets, label = None): isinstance(entry, BlockLabel.BlockLabel) or isinstance(entry, TdmInstructions.CustomInstruction) or #isinstance(entry, TdmInstructions.Instruction) or ?????? - isinstance(entry, TdmInstructions.LoadCmpVramInstruction)): + isinstance(entry, TdmInstructions.VRAMInstruction)): if isinstance(entry, BlockLabel.BlockLabel): # carry label forward to next entry label = entry @@ -850,8 +850,11 @@ def create_seq_instructions(seqs, offsets, label = None): except KeyError as e: raise Exception(f"Got unknown APS2 instruction: {e.args[0]} in {str(entry)}.") elif isinstance(entry, TdmInstructions.WriteAddrInstruction): + print(str(entry)) if entry.instruction == 'INVALIDATE' and entry.tdm == False: instructions.append(Invalidate(entry.addr, entry.value, label=label)) + if entry.instruction == 'WRITEADDR' and entry.tdm == False: + instructions.append(WriteAddr(entry.addr, entry.value, label=label)) label=None #Reset label to prevent it propagating in a jump table continue @@ -1543,7 +1546,8 @@ def display_raw_file(filename): colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), "MARKER": QColor(150,150,200), - "CUSTOM": QColor(200,65,200)} + "CUSTOM": QColor(200,65,200), + "WRITEADDR": QColor(245, 105, 65)} class MatplotlibWidget(QWidget): def __init__(self, I, Q, parent=None): From 418e1b47ad059c332aa86383e635270a080f3509 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 11:58:32 -0500 Subject: [PATCH 202/235] Add sequence debugger. --- QGL/test_helpers.py | 0 tests/aps_debug_watcher.py | 219 +++++++++++++++++++++++++++++++++++++ 2 files changed, 219 insertions(+) create mode 100644 QGL/test_helpers.py create mode 100644 tests/aps_debug_watcher.py diff --git a/QGL/test_helpers.py b/QGL/test_helpers.py new file mode 100644 index 00000000..e69de29b diff --git a/tests/aps_debug_watcher.py b/tests/aps_debug_watcher.py new file mode 100644 index 00000000..4f389e8f --- /dev/null +++ b/tests/aps_debug_watcher.py @@ -0,0 +1,219 @@ +#!/usr/bin/env python + +import sys +import socket +import struct +try: + from QGL.drivers import APS2Pattern + haveQGL = True +except: + haveQGL = False +import numpy as np +from ansicolor import * # from ansicolors + +ip = sys.argv[1] +port = 0xbb50 + +short_time = True + +raw_mode = '-r' in sys.argv +tdm_mode = '-tdm' in sys.argv + +print("Connecting to APS Debug Port at {0}:{1}".format(ip,port)) + +PACKET_SIZE=168//8+1 + +if short_time: + PACKET_SIZE -= 2 + +MODE_UNKNOWN = 0 +MODE_SEQUENCER = 1 +MODE_RAM = 2 +MODE_TRIGGER = 3 + +def bittest(v, b): + if ((v & (1< 0: + extraData = data[block_size:] + data = data[:block_size] + + for cnt in range(num_packets): + start = cnt * PACKET_SIZE + end = (cnt+1)*PACKET_SIZE + packet = data[start:end] + packet_bytes = bytearray(packet) + if raw_mode: + print(packet_bytes) + continue + + if packet[0] == 0x1: + mode = MODE_SEQUENCER + elif packet[0] in [0x2,0x3, 0x4, 0x5]: + mode = MODE_RAM + elif packet[0] == 0x6: + mode = MODE_TRIGGER + else: + mode = MODE_UNKNOWN + + + if short_time: + uptime_seconds = packet_bytes[1:3] + uptime_nanoseconds = packet_bytes[3:7] + + uptime_seconds = struct.unpack(">H", uptime_seconds)[0] + else: + uptime_seconds = packet_bytes[1:5] + uptime_nanoseconds = packet_bytes[5:9] + + uptime_seconds = struct.unpack(">I", uptime_seconds)[0] + + uptime_nanoseconds = struct.unpack(">I", uptime_nanoseconds)[0] + + #print(uptime_seconds, uptime_nanoseconds) + + uptime_nanoseconds = uptime_nanoseconds/1e9 + uptime = uptime_seconds + uptime_nanoseconds + + if short_time: + start = 8 + else: + start = 10 + + if mode == MODE_SEQUENCER: + + if short_time: + triggerWord = packet[7] + else: + triggerWord = packet[9] + + haltBits = packet[start] + + haltStr = formatHaltBits(haltBits) + + instructionAddr, start = get_bytes(packet, start, 4) + instructionAddr = bytearray(instructionAddr) + # clear halt bits, only the first two bits are part of the Addr + instructionAddr[0] = instructionAddr[0] & 0x3 + instructionAddr = struct.unpack(">I", instructionAddr)[0] + + seq_debug_data, start = get_bytes(packet, start, 8) + seq_debug_data = np.fromstring(seq_debug_data[::-1], dtype=np.uint64) + + if haveQGL: + instruction = APS2Pattern.Instruction.unflatten(seq_debug_data, decode_as_tdm = tdm_mode) + else: + instruction = '' + + h = "{:016x}".format(seq_debug_data[0]).upper() + + print(red("{:10}".format("Sequencer")), \ + white(": {:4}".format(instructionAddr)),\ + blue("0x{}".format(h)), \ + yellow(" {:6.8f}".format(uptime)), \ + haltStr, \ + "T:0x{:x}".format(triggerWord), \ + " {} ".format(instruction)) + # if instructionAddr > 100: + # sys.exit() + elif mode == MODE_RAM: + + zeros, start = get_bytes(packet, start, 4) + vram_addr, start = get_bytes(packet, start, 4) + vram_data, start = get_bytes(packet, start, 4) + + vram_header = struct.unpack(">I", zeros)[0] + vram_addr = struct.unpack(">I", vram_addr)[0] + vram_data = struct.unpack(">I", vram_data)[0] + + if packet[0] == 0x2: + ram_mode = "Valid" + + if packet[0] == 0x3: + ram_mode = "Write" + + if packet[0] == 0x4: + ram_mode = "Send" + + if packet[0] == 0x5: + ram_mode = "Recv" + + print(green("{:10}".format("RAM")), \ + white(": {:>23}".format(ram_mode)),\ + yellow(" {:6.8f}".format(uptime)), \ + "addr = 0x{:08X} data = 0x{:08X}".format(vram_addr, vram_data)) + + elif mode == MODE_TRIGGER: + + zeros, start = get_bytes(packet, start, 8) + syncBits, start = get_bytes(packet,start,1) + haltBits, start = get_bytes(packet, start, 1) + triggers, start = get_bytes(packet, start, 1) + triggerWord, start = get_bytes(packet,start,1) + + for z in zeros: + if z != 0: + print("Error expected 0 not", z) + + haltBits = haltBits[0] + syncBits = syncBits[0] + triggers = triggers[0] + triggerWord = triggerWord[0] + + haltStr = formatHaltBits(haltBits) + + syncWF = (syncBits >> 3) & 0xF + syncMarker = (syncBits >> 1) & 0x3 + syncMod = syncBits & 0x1 + + + halt = bittest(syncBits,4) + pc_jump = bittest(syncBits, 5) + tready = bittest(syncBits, 6) + tvalid = bittest(syncBits, 7) + + syncStr = "TV: {} TR: {} PJ: {} H: {} SWF: 0x{} SMa: 0x{} SMo: 0x{}".format(tvalid, tready, pc_jump, halt, syncWF, syncMarker, syncMod) + + trigger = bittest(triggers,1) + triggerWordValid = bittest(triggers,0) + + triggerStr = "t = {} tWV = {} tW = 0x{:0X}".format(trigger,triggerWordValid, triggerWord) + + print(green("{:10}".format("Trigger")), \ + white(": {:>23}".format('')),\ + yellow(" {:6.8f}".format(uptime)), \ + haltStr, \ + syncStr, \ + triggerStr) + elif mode == MODE_UNKNOWN: + print(green("{:10}".format("Unknown")),packet_bytes) + +s.close() From 3c8608d4b645c377671c46ad395b06a4594ece68 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:31:14 -0500 Subject: [PATCH 203/235] Properly set indirect mode on CALL --- QGL/APS2CustomInstructions.py | 4 ++-- QGL/ControlFlow.py | 6 +++--- QGL/drivers/APS2Pattern.py | 25 ++++++++++++++++++------- 3 files changed, 23 insertions(+), 12 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 3b269884..1d93cf38 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -61,13 +61,13 @@ def RandomClifford(target, addr): return [Invalidate(addr, 0, tdm=False), CustomInstruction("APS_CLIFFORD_RAND", 0x0, addr), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - Call(target, load_addr=True)] + Call(target, indirect=True)] def RandomCliffordInverse(target, addr): return [Invalidate(addr, 0, tdm=False), CustomInstruction("APS_CLIFFORD_INVERSE", 0x0, addr), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - Call(target, load_addr=True)] + Call(target, indirect=True)] def RandomCliffordInverseReset(addr): #for now don't use addr return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index daf12587..19d764dc 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -151,15 +151,15 @@ def __init__(self, target): class Call(ControlInstruction): # target is a BlockLabel - def __init__(self, target, load_addr=False): + def __init__(self, target, indirect=False): super(Call, self).__init__("CALL", target=target) - self.load_addr = load_addr + self.indirect = indirect def __str__(self): if self.load_addr: return f"{self.instruction}({str(self.target)})" else: - return f"{self.instruction}({str(self.target)}, LOAD)" + return f"{self.instruction}({str(self.target)}, INDIRECT)" diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 158abedf..bf48e0b1 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -342,12 +342,11 @@ def __str__(self): elif instrOpCode == LOADCMP: addr = self.payload & 0xFFFF mask = (self.payload >> 16) & 0xFFFF - use_ram = (self.payload >> 48) & 0x1 - if self.decode_as_tdm and not use_ram: + if self.decode_as_tdm and not self.use_ram: out += "WAITMEAS" else: src = "EXT" - if use_ram: + if self.use_ram: src = "RAM" out += "LOADCMP | source={0}, addr=0x{1:0x}, read_mask=0x{2:0x}".format(src, addr, mask) return out @@ -375,7 +374,17 @@ def writeFlag(self): @writeFlag.setter def writeFlag(self, value): - self.header |= value & 0x1 + self.header &= ~(0x1 << 0) #clear bit + self.header |= (value & 0x1) #set bit + + @property + def use_ram(self): + return ((self.payload >> 48) & 0x1) + + @use_ram.setter + def use_ram(self, value): + self.payload &= ~(0x1 << 48) #clear bit + self.payload |= ((value & 0x1) << 48) @property def opcode(self): @@ -448,8 +457,10 @@ def Goto(addr, label=None): return Command(GOTO, addr, label=label) -def Call(addr, load_addr=False, label=None): - return Command(CALL, addr, label=label, write=load_addr) +def Call(addr, indirect=False, label=None): + cmd = Command(CALL, addr, label=label) + cmd.use_ram = indirect + return cmd def Return(label=None): @@ -828,7 +839,7 @@ def create_seq_instructions(seqs, offsets, label = None): elif isinstance(entry, ControlFlow.Goto): instructions.append(Goto(entry.target, label=label)) elif isinstance(entry, ControlFlow.Call): - instructions.append(Call(entry.target, load_addr = entry.load_addr, label=label)) + instructions.append(Call(entry.target, indirect = entry.indirect, label=label)) elif isinstance(entry, ControlFlow.Repeat): instructions.append(Repeat(entry.target, label=label)) # value argument commands From 40fc869cb5e25818501227519200ccb5bd3646fe Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:55:27 -0500 Subject: [PATCH 204/235] Fix block labels on Calls. --- QGL/RandomCliffordTools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 6863f0b4..d4fc97bb 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -43,14 +43,14 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): if not isinstance(jt_label, BlockLabel.BlockLabel): raise ValueError("Jump table label must be a BlockLabel.") - cliff_labels = [f"C{n}" for n in range(24)] + cliff_labels = [BlockLabel.BlockLabel(f"C{n}") for n in range(24)] jump_table = [jt_label] for cl in cliff_labels: jump_table.append(ControlFlow.Call(cl)) jump_table.append(ControlFlow.Return()) for cliff, cl in zip(cliff_wires, cliff_labels): - cliff.insert(0, BlockLabel.BlockLabel(cl)) + cliff.insert(0, cl) cliff.append(ControlFlow.Return()) cliff_wires.insert(0, jump_table) From 20a83a72975badfbf87b0ae466cd01da4a403ad7 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:55:40 -0500 Subject: [PATCH 205/235] Make more pretty --- QGL/drivers/APS2Pattern.py | 33 +++++++++++++++++++++++++++++++-- 1 file changed, 31 insertions(+), 2 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index bf48e0b1..f3f6a97e 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1546,6 +1546,7 @@ def display_raw_file(filename): if __name__ == '__main__': if len(sys.argv) == 2: +<<<<<<< HEAD from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton from PyQt5.QtGui import QIcon, QColor, QFont @@ -1553,12 +1554,23 @@ def display_raw_file(filename): from matplotlib.figure import Figure table_font = QFont("Arial", weight=QFont.Bold) +======= + from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView + from PyQt5.QtGui import QIcon, QColor, QFont + + table_font = QFont("Arial", weight=57) +>>>>>>> Make more pretty colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), "MARKER": QColor(150,150,200), "CUSTOM": QColor(200,65,200), - "WRITEADDR": QColor(245, 105, 65)} + "WRITEADDR": QColor(245, 105, 65), + "INVALIDATE": QColor(245, 105, 65), + "CALL": QColor(65, 205, 245), + "RET": QColor(65, 205, 245), + "LOADCMP": QColor(245, 225, 65), + "MODULATION": QColor(175, 255, 185)} class MatplotlibWidget(QWidget): def __init__(self, I, Q, parent=None): @@ -1587,7 +1599,12 @@ def __init__(self, I, Q, parent=None): class App(QWidget): COLUMN_COUNT = 7 +<<<<<<< HEAD def __init__(self, instructions, waveforms): +======= + + def __init__(self, instructions): +>>>>>>> Make more pretty super().__init__() self.title = 'APS2 Disassembled Instructions' self.left = 100 @@ -1616,7 +1633,7 @@ def createTable(self): # Create table self.tableWidget = QTableWidget() self.tableWidget.setRowCount(len(self.instructions)) - self.tableWidget.setColumnCount(7) + self.tableWidget.setColumnCount(self.COLUMN_COUNT) for k, instr in enumerate(self.instructions): fields = str(instr).replace(',','').replace(';', '').split(" ") @@ -1628,6 +1645,7 @@ def createTable(self): color = None for l, f in enumerate(fields): text = fields[l] +<<<<<<< HEAD if text == "GOTO": btn = QPushButton(self.tableWidget) btn.setText('GOTO') @@ -1653,12 +1671,23 @@ def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.wavefor if color: item.setBackground(color) self.tableWidget.setItem(k, l, item) +======= + item = QTableWidgetItem(text) + item.setFont(table_font) + if color: + item.setBackground(color) + self.tableWidget.setItem(k,l, item) +>>>>>>> Make more pretty if l < self.COLUMN_COUNT-1: for j in range(l+1, self.COLUMN_COUNT): item = QTableWidgetItem("") if color: item.setBackground(color) self.tableWidget.setItem(k, j, item) +<<<<<<< HEAD +======= + +>>>>>>> Make more pretty self.tableWidget.move(0,0) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) From dc7d2be1ed379872b5c55f912ce4fd2fd616e312 Mon Sep 17 00:00:00 2001 From: gribeill Date: Tue, 26 Feb 2019 14:59:42 -0500 Subject: [PATCH 206/235] Bold font --- QGL/drivers/APS2Pattern.py | 46 +------------------------------------- 1 file changed, 1 insertion(+), 45 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index f3f6a97e..20f98b60 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1546,20 +1546,11 @@ def display_raw_file(filename): if __name__ == '__main__': if len(sys.argv) == 2: -<<<<<<< HEAD - from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton + from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView from PyQt5.QtGui import QIcon, QColor, QFont - from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - from matplotlib.figure import Figure - table_font = QFont("Arial", weight=QFont.Bold) -======= - from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView - from PyQt5.QtGui import QIcon, QColor, QFont - table_font = QFont("Arial", weight=57) ->>>>>>> Make more pretty colors = {"WFM": QColor(0,200,0), "GOTO": QColor(0,100,100), @@ -1599,12 +1590,8 @@ def __init__(self, I, Q, parent=None): class App(QWidget): COLUMN_COUNT = 7 -<<<<<<< HEAD - def __init__(self, instructions, waveforms): -======= def __init__(self, instructions): ->>>>>>> Make more pretty super().__init__() self.title = 'APS2 Disassembled Instructions' self.left = 100 @@ -1645,49 +1632,18 @@ def createTable(self): color = None for l, f in enumerate(fields): text = fields[l] -<<<<<<< HEAD - if text == "GOTO": - btn = QPushButton(self.tableWidget) - btn.setText('GOTO') - target_row = int(fields[1].split("=")[1]) - def scroll_to_goto_target(row=target_row, tab=self.tableWidget): - tab.scrollToItem(tab.item(row, 0)) - btn.clicked.connect(scroll_to_goto_target) - self.tableWidget.setCellWidget(k, l, btn) - if text == "WFM" and int(fields[4].split("=")[1])==0: - # Not a TA pair - btn = QPushButton(self.tableWidget) - btn.setText('WFM') - addr = int(fields[6].split("=")[1]) - count = int(fields[5].split("=")[1]) - def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): - w = MatplotlibWidget(I,Q) - self.plotters.append(w) - btn.clicked.connect(open_plotter) - self.tableWidget.setCellWidget(k, l, btn) - else: - item = QTableWidgetItem(text) - item.setFont(table_font) - if color: - item.setBackground(color) - self.tableWidget.setItem(k, l, item) -======= item = QTableWidgetItem(text) item.setFont(table_font) if color: item.setBackground(color) self.tableWidget.setItem(k,l, item) ->>>>>>> Make more pretty if l < self.COLUMN_COUNT-1: for j in range(l+1, self.COLUMN_COUNT): item = QTableWidgetItem("") if color: item.setBackground(color) self.tableWidget.setItem(k, j, item) -<<<<<<< HEAD -======= ->>>>>>> Make more pretty self.tableWidget.move(0,0) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) From 8ea186dc8716830293d2a98237010b7c3e70cb9b Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 26 Feb 2019 15:57:16 -0500 Subject: [PATCH 207/235] Added goto buttons and waveform plotters to APS2 disassembler --- QGL/drivers/APS2Pattern.py | 39 ++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 20f98b60..4f17a509 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1546,9 +1546,12 @@ def display_raw_file(filename): if __name__ == '__main__': if len(sys.argv) == 2: - from PyQt5.QtWidgets import QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView + from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton from PyQt5.QtGui import QIcon, QColor, QFont + from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg + from matplotlib.figure import Figure + table_font = QFont("Arial", weight=QFont.Bold) @@ -1591,7 +1594,7 @@ class App(QWidget): COLUMN_COUNT = 7 - def __init__(self, instructions): + def __init__(self, instructions, waveforms): super().__init__() self.title = 'APS2 Disassembled Instructions' self.left = 100 @@ -1632,11 +1635,31 @@ def createTable(self): color = None for l, f in enumerate(fields): text = fields[l] - item = QTableWidgetItem(text) - item.setFont(table_font) - if color: - item.setBackground(color) - self.tableWidget.setItem(k,l, item) + if text == "GOTO": + btn = QPushButton(self.tableWidget) + btn.setText('GOTO') + target_row = int(fields[1].split("=")[1]) + def scroll_to_goto_target(row=target_row, tab=self.tableWidget): + tab.scrollToItem(tab.item(row, 0)) + btn.clicked.connect(scroll_to_goto_target) + self.tableWidget.setCellWidget(k, l, btn) + if text == "WFM" and int(fields[4].split("=")[1])==0: + # Not a TA pair + btn = QPushButton(self.tableWidget) + btn.setText('WFM') + addr = int(fields[6].split("=")[1]) + count = int(fields[5].split("=")[1]) + def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): + w = MatplotlibWidget(I,Q) + self.plotters.append(w) + btn.clicked.connect(open_plotter) + self.tableWidget.setCellWidget(k, l, btn) + else: + item = QTableWidgetItem(text) + item.setFont(table_font) + if color: + item.setBackground(color) + self.tableWidget.setItem(k, l, item) if l < self.COLUMN_COUNT-1: for j in range(l+1, self.COLUMN_COUNT): item = QTableWidgetItem("") @@ -1646,7 +1669,7 @@ def createTable(self): self.tableWidget.move(0,0) self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) - + app = QApplication(sys.argv[:1]) ex = App(read_instructions(sys.argv[1]), read_waveforms(sys.argv[1])) sys.exit(app.exec_()) From c55f7e709bd43905080efe4d07d98cca512655fc Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Tue, 26 Feb 2019 21:34:53 -0500 Subject: [PATCH 208/235] Nice looking buttons for WFM and GOTO in disassembler --- QGL/drivers/APS2Pattern.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 4f17a509..801d95b2 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1603,7 +1603,6 @@ def __init__(self, instructions, waveforms): self.height = 1200 self.instructions = instructions self.waveforms = waveforms - print(self.waveforms) self.initUI() self.plotters = [] @@ -1642,17 +1641,19 @@ def createTable(self): def scroll_to_goto_target(row=target_row, tab=self.tableWidget): tab.scrollToItem(tab.item(row, 0)) btn.clicked.connect(scroll_to_goto_target) + btn.setStyleSheet("background-color: #006464; color: white; font-weight: bold; text-align: left;") self.tableWidget.setCellWidget(k, l, btn) if text == "WFM" and int(fields[4].split("=")[1])==0: # Not a TA pair btn = QPushButton(self.tableWidget) - btn.setText('WFM') + btn.setText(' WFM') addr = int(fields[6].split("=")[1]) count = int(fields[5].split("=")[1]) def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): w = MatplotlibWidget(I,Q) self.plotters.append(w) btn.clicked.connect(open_plotter) + btn.setStyleSheet("background-color: #00C800; color: white; font-weight: bold; text-align: left;") self.tableWidget.setCellWidget(k, l, btn) else: item = QTableWidgetItem(text) From 19abc55a63b9751fc64835f2b1cb3fd1a163d9f3 Mon Sep 17 00:00:00 2001 From: Graham Rowlands Date: Wed, 27 Feb 2019 10:21:02 -0500 Subject: [PATCH 209/235] Move disassembler to utils directory --- QGL/drivers/APS2Pattern.py | 132 ------------------------------- utils/disassemble_aps.py | 157 +++++++++++++++++++++++++++++++++++++ 2 files changed, 157 insertions(+), 132 deletions(-) create mode 100755 utils/disassemble_aps.py diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 801d95b2..f2e7090d 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1542,135 +1542,3 @@ def display_raw_instructions(raw): def display_raw_file(filename): raw = raw_instructions(filename) display_raw_instructions(raw) - -if __name__ == '__main__': - if len(sys.argv) == 2: - - from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton - from PyQt5.QtGui import QIcon, QColor, QFont - - from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg - from matplotlib.figure import Figure - - table_font = QFont("Arial", weight=QFont.Bold) - - - colors = {"WFM": QColor(0,200,0), - "GOTO": QColor(0,100,100), - "MARKER": QColor(150,150,200), - "CUSTOM": QColor(200,65,200), - "WRITEADDR": QColor(245, 105, 65), - "INVALIDATE": QColor(245, 105, 65), - "CALL": QColor(65, 205, 245), - "RET": QColor(65, 205, 245), - "LOADCMP": QColor(245, 225, 65), - "MODULATION": QColor(175, 255, 185)} - - class MatplotlibWidget(QWidget): - def __init__(self, I, Q, parent=None): - super(MatplotlibWidget, self).__init__(parent) - self.title = 'Waveform' - self.left = 100 - self.top = 100 - self.width = 800 - self.height = 600 - self.setWindowTitle(self.title) - self.setGeometry(self.left, self.top, self.width, self.height) - - self.figure = Figure() - self.canvas = FigureCanvasQTAgg(self.figure) - - self.axis = self.figure.add_subplot(111) - self.axis.plot(I) - self.axis.plot(Q) - self.layout = QVBoxLayout(self) - self.layout.addWidget(self.canvas) - self.setLayout(self.layout) - self.canvas.draw() - self.show() - - - class App(QWidget): - - COLUMN_COUNT = 7 - - def __init__(self, instructions, waveforms): - super().__init__() - self.title = 'APS2 Disassembled Instructions' - self.left = 100 - self.top = 100 - self.width = 1000 - self.height = 1200 - self.instructions = instructions - self.waveforms = waveforms - self.initUI() - self.plotters = [] - - def initUI(self): - self.setWindowTitle(self.title) - self.setGeometry(self.left, self.top, self.width, self.height) - - self.createTable() - self.layout = QVBoxLayout() - self.layout.addWidget(self.tableWidget) - self.setLayout(self.layout) - - # Show widget - self.show() - - def createTable(self): - # Create table - self.tableWidget = QTableWidget() - self.tableWidget.setRowCount(len(self.instructions)) - self.tableWidget.setColumnCount(self.COLUMN_COUNT) - - for k, instr in enumerate(self.instructions): - fields = str(instr).replace(',','').replace(';', '').split(" ") - if "|" in fields: - fields.remove("|") - if fields[0] in colors: - color = colors[fields[0]] - else: - color = None - for l, f in enumerate(fields): - text = fields[l] - if text == "GOTO": - btn = QPushButton(self.tableWidget) - btn.setText('GOTO') - target_row = int(fields[1].split("=")[1]) - def scroll_to_goto_target(row=target_row, tab=self.tableWidget): - tab.scrollToItem(tab.item(row, 0)) - btn.clicked.connect(scroll_to_goto_target) - btn.setStyleSheet("background-color: #006464; color: white; font-weight: bold; text-align: left;") - self.tableWidget.setCellWidget(k, l, btn) - if text == "WFM" and int(fields[4].split("=")[1])==0: - # Not a TA pair - btn = QPushButton(self.tableWidget) - btn.setText(' WFM') - addr = int(fields[6].split("=")[1]) - count = int(fields[5].split("=")[1]) - def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): - w = MatplotlibWidget(I,Q) - self.plotters.append(w) - btn.clicked.connect(open_plotter) - btn.setStyleSheet("background-color: #00C800; color: white; font-weight: bold; text-align: left;") - self.tableWidget.setCellWidget(k, l, btn) - else: - item = QTableWidgetItem(text) - item.setFont(table_font) - if color: - item.setBackground(color) - self.tableWidget.setItem(k, l, item) - if l < self.COLUMN_COUNT-1: - for j in range(l+1, self.COLUMN_COUNT): - item = QTableWidgetItem("") - if color: - item.setBackground(color) - self.tableWidget.setItem(k, j, item) - - self.tableWidget.move(0,0) - self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) - - app = QApplication(sys.argv[:1]) - ex = App(read_instructions(sys.argv[1]), read_waveforms(sys.argv[1])) - sys.exit(app.exec_()) diff --git a/utils/disassemble_aps.py b/utils/disassemble_aps.py new file mode 100755 index 00000000..1b7919c4 --- /dev/null +++ b/utils/disassemble_aps.py @@ -0,0 +1,157 @@ +#!/usr/bin/env python + +import numpy as np +import sys +import os.path + +from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton +from PyQt5.QtGui import QIcon, QColor, QFont +from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg +from matplotlib.figure import Figure + +colors = {"WFM": QColor(0,200,0), + "GOTO": QColor(0,100,100), + "MARKER": QColor(150,150,200), + "CUSTOM": QColor(200,65,200), + "WRITEADDR": QColor(245, 105, 65), + "INVALIDATE": QColor(245, 105, 65), + "CALL": QColor(65, 205, 245), + "RET": QColor(65, 205, 245), + "LOADCMP": QColor(245, 225, 65), + "MODULATION": QColor(175, 255, 185)} + +table_font = QFont("Arial", weight=QFont.Bold) + +class MatplotlibWidget(QWidget): + def __init__(self, I, Q, parent=None): + super(MatplotlibWidget, self).__init__(parent) + self.title = 'Waveform' + self.left = 100 + self.top = 100 + self.width = 800 + self.height = 600 + self.setWindowTitle(self.title) + self.setGeometry(self.left, self.top, self.width, self.height) + + self.figure = Figure() + self.canvas = FigureCanvasQTAgg(self.figure) + + self.axis = self.figure.add_subplot(111) + self.axis.plot(I) + self.axis.plot(Q) + self.layout = QVBoxLayout(self) + self.layout.addWidget(self.canvas) + self.setLayout(self.layout) + self.canvas.draw() + self.show() + + +class DisassemblerApp(QWidget): + + COLUMN_COUNT = 7 + + def __init__(self, instructions, waveforms): + super().__init__() + self.title = 'APS2 Disassembled Instructions' + self.left = 100 + self.top = 100 + self.width = 1000 + self.height = 1200 + self.instructions = instructions + self.waveforms = waveforms + self.initUI() + self.plotters = [] + + def initUI(self): + self.setWindowTitle(self.title) + self.setGeometry(self.left, self.top, self.width, self.height) + + self.createTable() + self.layout = QVBoxLayout() + self.layout.addWidget(self.tableWidget) + self.setLayout(self.layout) + + # Show widget + self.show() + + def createTable(self): + # Create table + self.tableWidget = QTableWidget() + self.tableWidget.setRowCount(len(self.instructions)) + self.tableWidget.setColumnCount(self.COLUMN_COUNT) + + for k, instr in enumerate(self.instructions): + fields = str(instr).replace(',','').replace(';', '').split(" ") + if "|" in fields: + fields.remove("|") + if fields[0] in colors: + color = colors[fields[0]] + else: + color = None + for l, f in enumerate(fields): + text = fields[l] + if text == "GOTO": + btn = QPushButton(self.tableWidget) + btn.setText('GOTO') + target_row = int(fields[1].split("=")[1]) + def scroll_to_goto_target(row=target_row, tab=self.tableWidget): + tab.scrollToItem(tab.item(row, 0)) + btn.clicked.connect(scroll_to_goto_target) + btn.setStyleSheet("background-color: #006464; color: white; font-weight: bold; text-align: left;") + self.tableWidget.setCellWidget(k, l, btn) + if text == "WFM" and int(fields[4].split("=")[1])==0: + # Not a TA pair + btn = QPushButton(self.tableWidget) + btn.setText(' WFM') + addr = int(fields[6].split("=")[1]) + count = int(fields[5].split("=")[1]) + def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): + w = MatplotlibWidget(I,Q) + self.plotters.append(w) + btn.clicked.connect(open_plotter) + btn.setStyleSheet("background-color: #00C800; color: white; font-weight: bold; text-align: left;") + self.tableWidget.setCellWidget(k, l, btn) + else: + item = QTableWidgetItem(text) + item.setFont(table_font) + if color: + item.setBackground(color) + self.tableWidget.setItem(k, l, item) + if l < self.COLUMN_COUNT-1: + for j in range(l+1, self.COLUMN_COUNT): + item = QTableWidgetItem("") + if color: + item.setBackground(color) + self.tableWidget.setItem(k, j, item) + + self.tableWidget.move(0,0) + self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) + +def get_target_hardware(filename): + with open(filename, 'rb') as FID: + target_hw = FID.read(4).decode('utf-8') + return target_hw + +if __name__ == '__main__': + if len(sys.argv) == 2: + filename = sys.argv[1] + try: + hw = get_target_hardware(filename) + if hw == "APS2": + from QGL.drivers.APS2Pattern import read_instructions, read_waveforms + app = QApplication(sys.argv[:1]) + ex = DisassemblerApp(read_instructions(sys.argv[1]), read_waveforms(sys.argv[1])) + sys.exit(app.exec_()) + elif hw == "APS1": + print("APS1 disassmbler not yet implemented") + else: + raise Exception("Unknown hardware target.") + except: + print(f"The supplied file {filename} does not appear to be APS1 or APS2 format.") + + else: + print("Expected single file (.aps1 or .aps2 format) as an argument.") + + + + From 9ba81a6156e28f0710469ba74134027c3d2cd672 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:54:39 -0500 Subject: [PATCH 210/235] Add jump table indicator to block label. --- QGL/BlockLabel.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/QGL/BlockLabel.py b/QGL/BlockLabel.py index 52db8b63..6725cd13 100644 --- a/QGL/BlockLabel.py +++ b/QGL/BlockLabel.py @@ -2,8 +2,10 @@ class BlockLabel(object): - def __init__(self, label): + def __init__(self, label, jump_table=False, table_size=0): self.label = label + self.jump_table = jump_table + self.table_size = table_size def __repr__(self): return self.__str__() From 79fffaaec19a68267a4a4c381d27446b8e898d26 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:56:09 -0500 Subject: [PATCH 211/235] Only set up clifford generator once per sequence file. --- QGL/RandomCliffordTools.py | 34 +++++++++++++++++++++++----------- 1 file changed, 23 insertions(+), 11 deletions(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index d4fc97bb..9317b3e2 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -33,13 +33,15 @@ from .APS2CustomInstructions import * from .Cliffords import C1, inverse_clifford, clifford_multiply +default_clifford_options = {"offset": 0x0, "spacing": 0x1, "seed": 0x31} + VALID_CLIFFORD_TYPES = ('RandomAC',) def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" if jt_label is None: - jt_label = BlockLabel.BlockLabel("JT") + jt_label = BlockLabel.BlockLabel("JT", jump_table=True, table_size=48) if not isinstance(jt_label, BlockLabel.BlockLabel): raise ValueError("Jump table label must be a BlockLabel.") @@ -55,14 +57,22 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): cliff_wires.insert(0, jump_table) + return jt_label + def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, - inv_addr=0x3, inv_values=[]): + inv_addr=0x3, inv_values=[], + clifford_options=default_clifford_options): if jt_label is None: jt_label = BlockLabel.BlockLabel("JT") if not isinstance(jt_label, BlockLabel.BlockLabel): raise ValueError("Jump table label must be a BlockLabel.") + #Insert defaults + for k,v in default_clifford_options.items(): + if k not in clifford_options.keys(): + cliford_options[k] = v + for idx, seq in enumerate(seqs[:]): if not PatternUtils.contains_runtime_pulses(seq): continue @@ -74,18 +84,14 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, and pulse.label in VALID_CLIFFORD_TYPES: has_random_cliff = True new_seq.extend(RandomClifford(jt_label, cliff_addr)) - print("Inserting clifford pulse!") + #print("Inserting clifford pulse!") else: new_seq.append(pulse) - info_seqs = [] - info_seqs.extend(RandomCliffordSetOffset(0x1, 0x0)) - info_seqs.extend(RandomCliffordSetSpacing(0x2, 0x1)) - - if isinstance(seq[0], BlockLabel.BlockLabel): - new_seq[1:1] = info_seqs - else: - new_seq[0:0] = info_seqs + # if isinstance(seq[0], BlockLabel.BlockLabel): + # new_seq[1:1] = info_seqs + # else: + # new_seq[0:0] = info_seqs if add_inv: #insert reset after first wait @@ -99,6 +105,12 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, seqs[idx] = new_seq + info_seqs = [] + info_seqs.extend(RandomCliffordSetOffset(0x1, clifford_options["offset"])) + info_seqs.extend(RandomCliffordSetSpacing(0x2, clifford_options["spacing"])) + info_seqs.extend(RandomCliffordSeed(clifford_options["seed"])) + seqs.insert(0, info_seqs) + def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): From 0545e72a84742235aca874c7fda0f55410aac55b Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:56:42 -0500 Subject: [PATCH 212/235] Correct __str__ for indicrect CALL --- QGL/ControlFlow.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/ControlFlow.py b/QGL/ControlFlow.py index 19d764dc..692958ff 100644 --- a/QGL/ControlFlow.py +++ b/QGL/ControlFlow.py @@ -156,7 +156,7 @@ def __init__(self, target, indirect=False): self.indirect = indirect def __str__(self): - if self.load_addr: + if not self.indirect: return f"{self.instruction}({str(self.target)})" else: return f"{self.instruction}({str(self.target)}, INDIRECT)" From 0b588e99a609df4b1bb5189841973510f40dcdf4 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:57:20 -0500 Subject: [PATCH 213/235] Correct implementation of APS2_SET_SEED --- QGL/APS2CustomInstructions.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 1d93cf38..ae9c4221 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -48,14 +48,14 @@ def RandomCliffordSetOffset(addr, offset): return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, offset, tdm=False), - LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0x3)] + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), # + CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0xA)] def RandomCliffordSetSpacing(addr, spacing): return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, spacing, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0x3)] + CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0xA)] def RandomClifford(target, addr): return [Invalidate(addr, 0, tdm=False), @@ -73,4 +73,7 @@ def RandomCliffordInverseReset(addr): #for now don't use addr return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) def RandomCliffordSeed(seed): - raise NotImplementedError("APS_CLIFFORD_SET_SEED not implemented!") + return [Invalidate(0xB, 0, tdm=False), + WriteAddr(0xB, seed, tdm=False), + LoadCmpVram(0xB, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_SET_SEED", 0xB, 0xA)] From 5382f144e380aa7ab255c7a205c5cd379ef18e11 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:58:15 -0500 Subject: [PATCH 214/235] Keep modulation commands explicitly in subroutines to prevent eliding Z pulses --- QGL/Compiler.py | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 930774d8..4071ae6d 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -37,6 +37,7 @@ from . import BlockLabel from . import TdmInstructions # only for APS2-TDM from . import APS2CustomInstructions +from .RandomCliffordTools import default_clifford_options from . import RandomCliffordTools import gc @@ -365,7 +366,8 @@ def collect_specializations(seqs): done.append(target) return funcs -def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords=False): +def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords=False, + clifford_options=default_clifford_options): ChannelLibraries.channelLib.update_channelDict() clear_pulse_cache() @@ -402,7 +404,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= # Compile all the pulses/pulseblocks to sequences of pulses and control flow logger.info("Compiling sequences.") - wireSeqs = compile_sequences(seqs, channels, random_cliffords=random_cliffords) + wireSeqs = compile_sequences(seqs, channels, random_cliffords=random_cliffords, clifford_options=clifford_options) if not validate_linklist_channels(wireSeqs.keys()): print("Compile to hardware failed") @@ -594,7 +596,8 @@ def compile_to_hardware(seqs, return metafilepath -def compile_sequences(seqs, channels=set(), random_cliffords=False): +def compile_sequences(seqs, channels=set(), random_cliffords=False, + clifford_options=default_clifford_options): ''' Main function to convert sequences to miniLL's and waveform libraries. ''' @@ -609,14 +612,21 @@ def compile_sequences(seqs, channels=set(), random_cliffords=False): seqs += subroutines if random_cliffords: + qubits = [q for q in list(channels) if isinstance(q, Channels.Qubit)] if len(qubits) > 1: raise Exception("Too many qubits! Don't know how to deal with this yet.") #replace cliffords with cliffords = [[get_clifford_type(seqs)(n)] for n in range(24)] - RandomCliffordTools.generate_clifford_jump_table(cliffords) - RandomCliffordTools.insert_clifford_calls(seqs) + jt_label = RandomCliffordTools.generate_clifford_jump_table(cliffords) + RandomCliffordTools.insert_clifford_calls(seqs, jt_label=jt_label, clifford_options=clifford_options) + + # if not isinstance(seqs[0][0], BlockLabel.BlockLabel): + # raise Exception("Fist instruciton must be block label!") + # start_label = seqs[0][0] seqs += cliffords + #seqs[0:0] = cliffords + #seqs[0].insert(0, ControlFlow.Goto(start_label)) #expand the channel definitions for anything defined in subroutines for func in subroutines: @@ -702,7 +712,14 @@ def flatten_to_pulses(obj): wires = propagate_node_frame_to_edges( wires, chan, block.pulses[chan].frameChange) # drop length 0 blocks but push nonzero frame changes onto previous entries - if block.length == 0: + + is_subroutine = False + if isinstance(seq[-1], ControlFlow.Return): + #Make sure we do not drop frame update instructions for subroutines that are only Z pulses + is_subroutine = True + + + if block.length == 0 and not is_subroutine: for chan in channels: if block.pulses[chan].frameChange == 0: continue From 3a2236954a22f2504cbf4ad3685e99396314538b Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 16:59:01 -0500 Subject: [PATCH 215/235] Proper prefetching for jump-table based subroutines --- QGL/drivers/APS2Pattern.py | 43 ++++++++++++++++++++++++++++---------- 1 file changed, 32 insertions(+), 11 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index f2e7090d..507da11d 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -298,8 +298,11 @@ def __str__(self): elif any( [instrOpCode == op for op in [GOTO, CALL, RET, REPEAT, PREFETCH]]): + if self.use_ram: + out += " RAM" out += " | target_addr={}".format(self.payload & 2**26 - 1) + elif instrOpCode == LOAD: out += " | count={}".format(self.payload) @@ -360,6 +363,9 @@ def __ne__(self, other): def __hash__(self): return hash((self.header, self.payload, self.label)) + def isa(self, value): + return (self.header >> 4) == value + @property def address(self): return self.payload & 0xffffffff # bottom 32-bits of payload @@ -623,7 +629,7 @@ def to_instruction(self, write_flag=True, label=None): instr.writeFlag = write_flag return instr -def inject_modulation_cmds(seqs, force_phase_update=False): +def inject_modulation_cmds(seqs): """ Inject modulation commands from phase, frequency and frameChange of waveforms in an IQ waveform sequence. Assume up to 2 NCOs for now. @@ -641,13 +647,17 @@ def inject_modulation_cmds(seqs, force_phase_update=False): frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] no_frame_cmds = np.all(np.less(np.abs(frame_changes), 1e-8)) no_modulation_cmds = no_freq_cmds and no_phase_cmds and no_frame_cmds - #import pdb; pdb.set_trace() if no_modulation_cmds: continue mod_seq = [] pending_frame_update = False + #check to see if we are in a subroutine by using last instruction as return as a tell + #if so, ensure frame updates do not get dropped + if isinstance(seq[-1], ControlFlow.Return): + force_phase_update = True + for entry in seq: #copies to avoid same object having different timestamps later @@ -735,7 +745,6 @@ def synchronize_clocks(seqs): syncInstructions = [list(filter( lambda s: isinstance(s, ControlFlow.ControlInstruction), seq)) for seq in seqs if seq] - #import pdb; pdb.set_trace() # Add length to control-flow instructions to make accumulated time match at end of CFI. # Keep running tally of how much each channel has been shifted so far. localShift = [0 for _ in syncInstructions] @@ -854,14 +863,14 @@ def create_seq_instructions(seqs, offsets, label = None): instructions.append(LoadCmpVram(entry.addr, entry.mask, label=label)) # some TDM instructions are ignored by the APS elif isinstance(entry, TdmInstructions.CustomInstruction): - print(str(entry)) + #print(str(entry)) try: instructions.append(Custom(entry.in_addr, entry.out_addr, APS2_CUSTOM[entry.instruction], label=label)) except KeyError as e: raise Exception(f"Got unknown APS2 instruction: {e.args[0]} in {str(entry)}.") elif isinstance(entry, TdmInstructions.WriteAddrInstruction): - print(str(entry)) + #print(str(entry)) if entry.instruction == 'INVALIDATE' and entry.tdm == False: instructions.append(Invalidate(entry.addr, entry.value, label=label)) if entry.instruction == 'WRITEADDR' and entry.tdm == False: @@ -946,7 +955,6 @@ def create_instr_data(seqs, offsets, cache_lines): except: pass instructions += seq - #if we have any subroutines then group in cache lines if subroutines_start >= 0: subroutine_instrs = [] @@ -974,12 +982,25 @@ def create_instr_data(seqs, offsets, cache_lines): CACHE_LINE_LENGTH)) #inject prefetch commands before waits wait_idx = [idx for idx, instr in enumerate(instructions) - if (instr.header >> 4) == WAIT] + [len(instructions)] + if instr.isa(WAIT)] + [len(instructions)] instructions_with_prefetch = instructions[:wait_idx[0]] last_prefetch = None + for start, stop in zip(wait_idx[:-1], wait_idx[1:]): call_targets = [instr.target for instr in instructions[start:stop] - if (instr.header >> 4) == CALL] + if instr.isa(CALL)] + + #Check if we call into any jump tables + jump_tables = [label for label in call_targets if label.jump_table] + if len(jump_tables) > 0: + for jt in set(jump_tables): + #Find the start of the jump table + start_idx = next(j for j, instr in enumerate(subroutine_instrs) + if instr.label == jt) + stop_idx = start_idx + jt.table_size + call_targets.extend([instr.target for instr in + subroutine_instrs[start_idx:stop_idx] + if instr.isa(CALL)]) needed_lines = set() for target in call_targets: needed_lines.add(subroutine_cache_line[target]) @@ -996,9 +1017,9 @@ def create_instr_data(seqs, offsets, cache_lines): #pad out instruction vector to ensure circular cache never loads a subroutine pad_instrs = 7 * 128 + (128 - ((len(instructions) + 128) % 128)) instructions += [NoOp()] * pad_instrs - instructions += subroutine_instrs + #turn symbols into integers addresses resolve_symbols(instructions) @@ -1013,7 +1034,7 @@ def resolve_symbols(seq): labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] symbols = {label: idx for idx, label in labeled_entries} - print(f"Found labels: {symbols}") + #print(f"Found labels: {symbols}") for entry in seq: if entry.target is not None and entry.target in symbols.keys(): entry.address = symbols[entry.target] @@ -1215,7 +1236,7 @@ def start_new_seq(): chan_select_bits = ((instr.header >> 2) & 0x1, (instr.header >> 3) & 0x1) #On older firmware we broadcast by default whereas on newer we respect the engine select - for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits): + for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits):plot_pulse if (file_version < 4) or select_bit: if isTA: seqs[chan][-1].append((count, wf_lib[chan][addr])) From d45839b505510820943924b6ddf48eb44db32f95 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 17:00:52 -0500 Subject: [PATCH 216/235] Fix typo. --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 507da11d..c5b0762f 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1236,7 +1236,7 @@ def start_new_seq(): chan_select_bits = ((instr.header >> 2) & 0x1, (instr.header >> 3) & 0x1) #On older firmware we broadcast by default whereas on newer we respect the engine select - for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits):plot_pulse + for chan, select_bit in zip(('ch1', 'ch2'), chan_select_bits): if (file_version < 4) or select_bit: if isTA: seqs[chan][-1].append((count, wf_lib[chan][addr])) From bd19ab381ab30ec42fc21152ded61a0af4e5522a Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 17:05:06 -0500 Subject: [PATCH 217/235] Remove debug watcher from QGL (now in libaps2) --- tests/aps_debug_watcher.py | 219 ------------------------------------- thing.txt | 3 + 2 files changed, 3 insertions(+), 219 deletions(-) delete mode 100644 tests/aps_debug_watcher.py create mode 100644 thing.txt diff --git a/tests/aps_debug_watcher.py b/tests/aps_debug_watcher.py deleted file mode 100644 index 4f389e8f..00000000 --- a/tests/aps_debug_watcher.py +++ /dev/null @@ -1,219 +0,0 @@ -#!/usr/bin/env python - -import sys -import socket -import struct -try: - from QGL.drivers import APS2Pattern - haveQGL = True -except: - haveQGL = False -import numpy as np -from ansicolor import * # from ansicolors - -ip = sys.argv[1] -port = 0xbb50 - -short_time = True - -raw_mode = '-r' in sys.argv -tdm_mode = '-tdm' in sys.argv - -print("Connecting to APS Debug Port at {0}:{1}".format(ip,port)) - -PACKET_SIZE=168//8+1 - -if short_time: - PACKET_SIZE -= 2 - -MODE_UNKNOWN = 0 -MODE_SEQUENCER = 1 -MODE_RAM = 2 -MODE_TRIGGER = 3 - -def bittest(v, b): - if ((v & (1< 0: - extraData = data[block_size:] - data = data[:block_size] - - for cnt in range(num_packets): - start = cnt * PACKET_SIZE - end = (cnt+1)*PACKET_SIZE - packet = data[start:end] - packet_bytes = bytearray(packet) - if raw_mode: - print(packet_bytes) - continue - - if packet[0] == 0x1: - mode = MODE_SEQUENCER - elif packet[0] in [0x2,0x3, 0x4, 0x5]: - mode = MODE_RAM - elif packet[0] == 0x6: - mode = MODE_TRIGGER - else: - mode = MODE_UNKNOWN - - - if short_time: - uptime_seconds = packet_bytes[1:3] - uptime_nanoseconds = packet_bytes[3:7] - - uptime_seconds = struct.unpack(">H", uptime_seconds)[0] - else: - uptime_seconds = packet_bytes[1:5] - uptime_nanoseconds = packet_bytes[5:9] - - uptime_seconds = struct.unpack(">I", uptime_seconds)[0] - - uptime_nanoseconds = struct.unpack(">I", uptime_nanoseconds)[0] - - #print(uptime_seconds, uptime_nanoseconds) - - uptime_nanoseconds = uptime_nanoseconds/1e9 - uptime = uptime_seconds + uptime_nanoseconds - - if short_time: - start = 8 - else: - start = 10 - - if mode == MODE_SEQUENCER: - - if short_time: - triggerWord = packet[7] - else: - triggerWord = packet[9] - - haltBits = packet[start] - - haltStr = formatHaltBits(haltBits) - - instructionAddr, start = get_bytes(packet, start, 4) - instructionAddr = bytearray(instructionAddr) - # clear halt bits, only the first two bits are part of the Addr - instructionAddr[0] = instructionAddr[0] & 0x3 - instructionAddr = struct.unpack(">I", instructionAddr)[0] - - seq_debug_data, start = get_bytes(packet, start, 8) - seq_debug_data = np.fromstring(seq_debug_data[::-1], dtype=np.uint64) - - if haveQGL: - instruction = APS2Pattern.Instruction.unflatten(seq_debug_data, decode_as_tdm = tdm_mode) - else: - instruction = '' - - h = "{:016x}".format(seq_debug_data[0]).upper() - - print(red("{:10}".format("Sequencer")), \ - white(": {:4}".format(instructionAddr)),\ - blue("0x{}".format(h)), \ - yellow(" {:6.8f}".format(uptime)), \ - haltStr, \ - "T:0x{:x}".format(triggerWord), \ - " {} ".format(instruction)) - # if instructionAddr > 100: - # sys.exit() - elif mode == MODE_RAM: - - zeros, start = get_bytes(packet, start, 4) - vram_addr, start = get_bytes(packet, start, 4) - vram_data, start = get_bytes(packet, start, 4) - - vram_header = struct.unpack(">I", zeros)[0] - vram_addr = struct.unpack(">I", vram_addr)[0] - vram_data = struct.unpack(">I", vram_data)[0] - - if packet[0] == 0x2: - ram_mode = "Valid" - - if packet[0] == 0x3: - ram_mode = "Write" - - if packet[0] == 0x4: - ram_mode = "Send" - - if packet[0] == 0x5: - ram_mode = "Recv" - - print(green("{:10}".format("RAM")), \ - white(": {:>23}".format(ram_mode)),\ - yellow(" {:6.8f}".format(uptime)), \ - "addr = 0x{:08X} data = 0x{:08X}".format(vram_addr, vram_data)) - - elif mode == MODE_TRIGGER: - - zeros, start = get_bytes(packet, start, 8) - syncBits, start = get_bytes(packet,start,1) - haltBits, start = get_bytes(packet, start, 1) - triggers, start = get_bytes(packet, start, 1) - triggerWord, start = get_bytes(packet,start,1) - - for z in zeros: - if z != 0: - print("Error expected 0 not", z) - - haltBits = haltBits[0] - syncBits = syncBits[0] - triggers = triggers[0] - triggerWord = triggerWord[0] - - haltStr = formatHaltBits(haltBits) - - syncWF = (syncBits >> 3) & 0xF - syncMarker = (syncBits >> 1) & 0x3 - syncMod = syncBits & 0x1 - - - halt = bittest(syncBits,4) - pc_jump = bittest(syncBits, 5) - tready = bittest(syncBits, 6) - tvalid = bittest(syncBits, 7) - - syncStr = "TV: {} TR: {} PJ: {} H: {} SWF: 0x{} SMa: 0x{} SMo: 0x{}".format(tvalid, tready, pc_jump, halt, syncWF, syncMarker, syncMod) - - trigger = bittest(triggers,1) - triggerWordValid = bittest(triggers,0) - - triggerStr = "t = {} tWV = {} tW = 0x{:0X}".format(trigger,triggerWordValid, triggerWord) - - print(green("{:10}".format("Trigger")), \ - white(": {:>23}".format('')),\ - yellow(" {:6.8f}".format(uptime)), \ - haltStr, \ - syncStr, \ - triggerStr) - elif mode == MODE_UNKNOWN: - print(green("{:10}".format("Unknown")),packet_bytes) - -s.close() diff --git a/thing.txt b/thing.txt new file mode 100644 index 00000000..5e24153f --- /dev/null +++ b/thing.txt @@ -0,0 +1,3 @@ +1.222 4.0+5.0i +1.32 4.1+5.6i +1.55 4.2+6.7i From d34bb118e5f7ef17e14438e152fec784dc27a358 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 27 Feb 2019 17:05:49 -0500 Subject: [PATCH 218/235] Remove junk --- thing.txt | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 thing.txt diff --git a/thing.txt b/thing.txt deleted file mode 100644 index 5e24153f..00000000 --- a/thing.txt +++ /dev/null @@ -1,3 +0,0 @@ -1.222 4.0+5.0i -1.32 4.1+5.6i -1.55 4.2+6.7i From 8d75d89cf9557ab3a59aef00f36cc410ca78386b Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Wed, 24 Apr 2019 17:36:37 -0400 Subject: [PATCH 219/235] Update to latest sqlalchemy syntax --- QGL/Compiler.py | 64 +++++++++++++++++++++++++++++-------------------- 1 file changed, 38 insertions(+), 26 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 4071ae6d..04a6bfe8 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -432,26 +432,32 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= physWires = map_logical_to_physical(wireSeqs) # Pave the way for composite instruments, not useful yet... - files = {} - label_to_inst = {} - label_to_chan = {} - old_wire_names = {} - old_wire_instrs = {} + extra_info = {} + extra_info["files"] = {} + extra_info["label_to_inst"] = {} + extra_info["label_to_chan"] = {} + extra_info["old_wire_names"] = {} + extra_info["old_wire_instrs"] = {} + + extra_info["num_measurements"] = num_measurements + extra_info["wire_measurements"] = wire_measurements + extra_info["channels"] = channels + for wire, pulses in physWires.items(): pattern_module = import_module('QGL.drivers.' + wire.translator) if pattern_module.SEQFILE_PER_CHANNEL: inst_name = wire.transmitter.label chan_name = wire.label has_non_id_pulses = any([len([p for p in ps if isinstance(p,Pulse) and p.label!="Id"]) > 0 for ps in pulses]) - label_to_inst[wire.label] = inst_name + extra_info["label_to_inst"][wire.label] = inst_name if has_non_id_pulses: - label_to_chan[wire.label] = chan_name + extra_info["label_to_chan"][wire.label] = chan_name # Change the name/inst for uniqueness, but we must restore this later! - old_wire_names[wire] = wire.label - old_wire_instrs[wire] = wire.instrument + extra_info["old_wire_names"][wire] = wire.label + extra_info["old_wire_instrs"][wire] = wire.instrument wire.instrument = wire.label wire.label = chan_name - #files[inst_name] = {} + extra_info["files"][inst_name] = {} # construct channel delay map logger.info("Constructing delay map.") @@ -479,7 +485,7 @@ def compile_to_IR(seqs, add_slave_trigger=True, tdm_seq=False, random_cliffords= # on whether we have one sequence per channel logger.info("Bundling wires.") awgData = bundle_wires(physWires, wfs) - return awgData + return awgData, extra_info def compile_to_hardware(seqs, fileName, @@ -505,9 +511,15 @@ def compile_to_hardware(seqs, tdm_seq (optional): compile for TDM ''' # save input code to file + save_code(seqs, fileName + suffix) - awgData = compile_to_IR(seqs, add_slave_trigger=add_slave_trigger, tdm_seq=tdm_seq) + if any([p.isRunTime for p in flatten(seqs)]): + random_cliffords = True + else: + random_cliffords = False + + awgData, extra_info = compile_to_IR(seqs, add_slave_trigger=add_slave_trigger, tdm_seq=tdm_seq, random_cliffords=random_cliffords) # convert to hardware formats # files = {} @@ -529,11 +541,11 @@ def compile_to_hardware(seqs, ChannelLibraries.channelLib[awgName].extra_meta = new_meta # Allow for per channel and per AWG seq files - if awgName in label_to_inst: - if awgName in label_to_chan: - files[label_to_chan[awgName]] = fullFileName + if awgName in extra_info["label_to_inst"]: + if awgName in extra_info["label_to_chan"]: + files[extra_info["label_to_inst"][awgName]][extra_info["label_to_chan"][awgName]] = fullFileName else: - files[awgName] = fullFileName + extra_info["files"][awgName] = fullFileName del data del awgData[awgName] @@ -563,21 +575,21 @@ def compile_to_hardware(seqs, axis_descriptor = [{ 'name': 'segment', 'unit': None, - 'points': list(range(1, 1 + num_measurements)), + 'points': list(range(1, 1 + extra_info["num_measurements"])), 'partition': 1 }] receiver_measurements = {} - for wire, n in wire_measurements.items(): + for wire, n in extra_info["wire_measurements"].items(): if wire.receiver_chan and n>0: receiver_measurements[wire.receiver_chan.label] = n meta = { 'database_info': db_info, - 'instruments': files, + 'instruments': extra_info["files"], 'num_sequences': len(seqs), - 'num_measurements': num_measurements, + 'num_measurements': extra_info["num_measurements"], 'axis_descriptor': axis_descriptor, - 'qubits': [c.label for c in channels if isinstance(c, Channels.Qubit)], - 'measurements': [c.label for c in channels if isinstance(c, Channels.Measurement)], + 'qubits': [c.label for c in extra_info["channels"] if isinstance(c, Channels.Qubit)], + 'measurements': [c.label for c in extra_info["channels"] if isinstance(c, Channels.Measurement)], 'receivers': receiver_measurements } if extra_meta: @@ -587,10 +599,10 @@ def compile_to_hardware(seqs, json.dump(meta, FID, indent=2, sort_keys=True) # Restore the wire info - for wire in old_wire_names.keys(): - wire.label = old_wire_names[wire] - for wire in old_wire_instrs.keys(): - wire.instrument = old_wire_instrs[wire] + for wire in extra_info["old_wire_names"].keys(): + wire.label = extra_info["old_wire_names"][wire] + for wire in extra_info["old_wire_instrs"].keys(): + wire.instrument = extra_info["old_wire_instrs"][wire] # Return the filenames we wrote return metafilepath From dba195f27bcf1611f5c4a5059f87f47cb4d61301 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 26 Apr 2019 16:06:16 -0400 Subject: [PATCH 220/235] Add python egg file to gitignore --- .gitignore | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitignore b/.gitignore index fcfad9a8..7150e5b0 100644 --- a/.gitignore +++ b/.gitignore @@ -5,3 +5,5 @@ *.h5 *.py~ *.py# +*.egg-info +QGL.egg-info/ From dcde87bdc6705f264c5e4dba4af7bc4649827c3c Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 26 Apr 2019 16:07:25 -0400 Subject: [PATCH 221/235] Update Inverse Reset instruction sequence. --- QGL/APS2CustomInstructions.py | 7 +++++-- QGL/RandomCliffordTools.py | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index ae9c4221..9b8268f9 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -69,8 +69,11 @@ def RandomCliffordInverse(target, addr): LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), Call(target, indirect=True)] -def RandomCliffordInverseReset(addr): #for now don't use addr - return CustomInstruction("APS_CLIFFORD_INVERSE_RESET", 0x0, 0xF) +def RandomCliffordInverseReset(val=0x0, addr=0xD): #for now don't use addr + return [Invalidate(addr, 0, tdm=False), + WriteAddr(addr, val, tdm=False), + LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), + CustomInstruction("APS_CLIFFORD_INVERSE_RESET", addr, 0xA)] def RandomCliffordSeed(seed): return [Invalidate(0xB, 0, tdm=False), diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 9317b3e2..82288031 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -60,7 +60,7 @@ def generate_clifford_jump_table(cliff_wires, jt_label = None): return jt_label def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, - inv_addr=0x3, inv_values=[], + inv_addr=0x4, inv_values=[], clifford_options=default_clifford_options): if jt_label is None: From 8c6630bd65ed720f4bd7930395bc5476714f886e Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 26 Apr 2019 16:08:56 -0400 Subject: [PATCH 222/235] Output label names and address for debug. --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index c5b0762f..a833780f 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1034,7 +1034,7 @@ def resolve_symbols(seq): labeled_entries = [(idx, entry.label) for idx, entry in enumerate(seq) if entry.label is not None] symbols = {label: idx for idx, label in labeled_entries} - #print(f"Found labels: {symbols}") + logger.info(f"Found labels: {symbols}") for entry in seq: if entry.target is not None and entry.target in symbols.keys(): entry.address = symbols[entry.target] From e7b6aacf1a3359d142ffdab691aeb8534c9b4b07 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Fri, 19 Jul 2019 19:15:50 -0400 Subject: [PATCH 223/235] Add RandomClifford to set of random pulses. -GR --- QGL/Compiler.py | 2 +- QGL/PulsePrimitives.py | 12 ++++++++++-- QGL/RandomCliffordTools.py | 2 +- QGL/drivers/APS2Pattern.py | 1 + 4 files changed, 13 insertions(+), 4 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 04a6bfe8..612357bd 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -236,7 +236,7 @@ def generate_waveforms(physicalWires): wfs[ch][pulse.hashshape()] = pulse.shape return wfs -def get_clifford_type(wire, allowed_types=("RandomAC")): +def get_clifford_type(wire, allowed_types=("RandomAC", "RandomClifford")): rt_pulses = [] for pulse in flatten(wire): if isinstance(pulse, Pulse) and pulse.isRunTime: diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index a648e044..a4c879a3 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -24,7 +24,6 @@ from .PulseSequencer import Pulse, TAPulse, CompoundGate, align from functools import wraps, reduce - def overrideDefaults(chan, updateParams, ignoredStrParams=['isRunTime', 'maddr', 'moffset']): '''Helper function to update any parameters passed in and fill in the defaults otherwise.''' # The default parameter list depends on the channel type so pull out of channel @@ -116,7 +115,7 @@ def Utheta(qubit, else: # linearly scale based upon the 'pi/2' amplitude amp = (angle / (pi/2)) * qubit.pulse_params['pi2Amp'] - return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency, **kwargs) + return Pulse(label, qubit, params, amp, phase, 0.0, ignoredStrParams, frequency=frequency) # generic pulses around X, Y, and Z axes @@ -340,6 +339,10 @@ def arb_axis_drag(qubit, return Pulse(kwargs["label"] if "label" in kwargs else "ArbAxis", qubit, params, 1.0, aziAngle, frameChange) +def RandomClifford(qubit): + params = overrideDefaults(qubit, {}) + return Pulse("RandomClifford", qubit, params, isRunTime=True) + def RandomAC(qubit): params = overrideDefaults(qubit, {}) return Pulse("RandomAC", qubit, params, isRunTime=True) @@ -349,6 +352,11 @@ def RandomDiAC(qubit): params["length"] *= 2.0 return Pulse("RandomDiAC", qubit, params, isRunTime=True) +from . import Cliffords + +def Clifford(qubit, cliffNum): + return reduce(operator.add, [p for p in Cliffords.clifford_seq(cliffNum, qubit)]) + def AC(qubit, cliffNum): """ diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 82288031..6d202835 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -35,7 +35,7 @@ default_clifford_options = {"offset": 0x0, "spacing": 0x1, "seed": 0x31} -VALID_CLIFFORD_TYPES = ('RandomAC',) +VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomClifford') def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index a833780f..20bb84fc 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -652,6 +652,7 @@ def inject_modulation_cmds(seqs): mod_seq = [] pending_frame_update = False + force_phase_update = False #check to see if we are in a subroutine by using last instruction as return as a tell #if so, ensure frame updates do not get dropped From e44559201c988c6c54d02851003f5919e8819e6b Mon Sep 17 00:00:00 2001 From: gribeill Date: Wed, 24 Jul 2019 17:18:40 -0400 Subject: [PATCH 224/235] Revert 4-NCO changes to APS2Pattern.py --- QGL/drivers/APS2Pattern.py | 29 +++++++++++------------------ 1 file changed, 11 insertions(+), 18 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 20bb84fc..863e78ae 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -599,19 +599,13 @@ def to_instruction(self, write_flag=True, label=None): NCO_SELECT_OP_OFFSET = 40 MODULATION_CLOCK = 300e6 - nco_select_bits = {1 : 0b0001, - 2 : 0b0010, - 3 : 0b0100, - 4 : 0b1000, - 15: 0b1111}[self.nco_select] - op_code_map = {"MODULATE": 0x0, "RESET_PHASE": 0x2, "SET_FREQ": 0x6, "SET_PHASE": 0xa, "UPDATE_FRAME": 0xe} payload = (op_code_map[self.instruction] << MODULATOR_OP_OFFSET) | ( - (nco_select_bits) << NCO_SELECT_OP_OFFSET) + (self.nco_select) << NCO_SELECT_OP_OFFSET) if self.instruction == "MODULATE": #zero-indexed quad count payload |= np.uint32(self.length / ADDRESS_UNIT - 1) @@ -639,9 +633,9 @@ def inject_modulation_cmds(seqs): for ct,seq in enumerate(seqs): #check whether we have modulation commands freqs = np.unique([entry.frequency for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)]) - if len(freqs) > NUM_NCO: - raise Exception("Max {} frequencies on the same channel allowed.".format(NUM_NCO)) - no_freq_cmds = np.allclose(freqs, 0) + if len(freqs) > 2: + raise Exception("Max {} frequencies on the same channel allowed.".format(2)) + no_freq_cmds = np.all(np.less(np.abs(freqs), 1e-8)) phases = [entry.phase for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] no_phase_cmds = np.all(np.less(np.abs(phases), 1e-8)) frame_changes = [entry.frameChange for entry in filter(lambda s: isinstance(s,Compiler.Waveform), seq)] @@ -670,7 +664,7 @@ def inject_modulation_cmds(seqs): #heuristic to insert phase reset before trigger if we have modulation commands if isinstance(entry, ControlFlow.Wait): if not ( no_modulation_cmds and (cur_freq == 0) and (cur_phase == 0)): - mod_seq.append(ModulationCommand("RESET_PHASE", 0xF)) + mod_seq.append(ModulationCommand("RESET_PHASE", 0x3)) for nco_ind, freq in enumerate(freqs): mod_seq.append( ModulationCommand("SET_FREQ", nco_ind + 1, frequency = -freq) ) elif isinstance(entry, ControlFlow.Return): @@ -1191,13 +1185,12 @@ def start_new_seq(): instructions = np.frombuffer(FID.read(8*inst_len), dtype=np.uint64) wf_lib = {} - wf_lib['ch1'] = ( - 1.0 / - MAX_WAVEFORM_VALUE) * FID['/chan_1/waveforms'].value.flatten() - wf_lib['ch2'] = ( - 1.0 / - MAX_WAVEFORM_VALUE) * FID['/chan_2/waveforms'].value.flatten() - instructions = FID['/chan_1/instructions'].value.flatten() + for i in range(num_chans): + wf_len = struct.unpack(' Date: Wed, 24 Jul 2019 17:20:26 -0400 Subject: [PATCH 225/235] Remove RandomClifford name collison --- QGL/Compiler.py | 4 ++-- QGL/PulsePrimitives.py | 4 ++-- QGL/RandomCliffordTools.py | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index 612357bd..a104e066 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -37,7 +37,7 @@ from . import BlockLabel from . import TdmInstructions # only for APS2-TDM from . import APS2CustomInstructions -from .RandomCliffordTools import default_clifford_options +from .RandomCliffordTools import default_clifford_options, VALID_CLIFFORD_TYPES from . import RandomCliffordTools import gc @@ -236,7 +236,7 @@ def generate_waveforms(physicalWires): wfs[ch][pulse.hashshape()] = pulse.shape return wfs -def get_clifford_type(wire, allowed_types=("RandomAC", "RandomClifford")): +def get_clifford_type(wire, allowed_types=VALID_CLIFFORD_TYPES): rt_pulses = [] for pulse in flatten(wire): if isinstance(pulse, Pulse) and pulse.isRunTime: diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index a4c879a3..5627d6d3 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -339,9 +339,9 @@ def arb_axis_drag(qubit, return Pulse(kwargs["label"] if "label" in kwargs else "ArbAxis", qubit, params, 1.0, aziAngle, frameChange) -def RandomClifford(qubit): +def RandomCliff(qubit): params = overrideDefaults(qubit, {}) - return Pulse("RandomClifford", qubit, params, isRunTime=True) + return Pulse("RandomCliff", qubit, params, isRunTime=True) def RandomAC(qubit): params = overrideDefaults(qubit, {}) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 6d202835..df848aa2 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -35,7 +35,7 @@ default_clifford_options = {"offset": 0x0, "spacing": 0x1, "seed": 0x31} -VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomClifford') +VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomCliff') def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" From 2589b4426a384448c1adbbf1392dccc0044ebacd Mon Sep 17 00:00:00 2001 From: Matthew Ware Date: Thu, 25 Jul 2019 10:27:49 -0400 Subject: [PATCH 226/235] typo in APS2pattern.py -MW --- QGL/drivers/APS2Pattern.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 863e78ae..f5ff31de 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1188,7 +1188,7 @@ def start_new_seq(): for i in range(num_chans): wf_len = struct.unpack(' Date: Thu, 25 Jul 2019 10:31:52 -0400 Subject: [PATCH 227/235] Let's be honest about what we're doing -MW --- QGL/drivers/APS2Pattern.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index f5ff31de..669f4563 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -1,5 +1,5 @@ ''' -Module for writing hdf5 APS2 files from sequences and patterns +Module for writing .aps APS2 files from sequences and patterns Copyright 2014 Raytheon BBN Technologies @@ -1158,7 +1158,7 @@ def write_sequence_file(awgData, fileName): def read_sequence_file(fileName): """ - Reads a HDF5 sequence file and returns a dictionary of lists. + Reads a .aps sequence file and returns a dictionary of lists. Dictionary keys are channel strings such as ch1, m1 Lists are or tuples of time-amplitude pairs (time, output) """ From 4a53e7bc8b3b12af522f1854ac3d305654e7f187 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 29 Jul 2019 13:36:18 -0400 Subject: [PATCH 228/235] Load to comparison register after custom instruction to block sequencer. --- QGL/APS2CustomInstructions.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/QGL/APS2CustomInstructions.py b/QGL/APS2CustomInstructions.py index 9b8268f9..971bd16b 100644 --- a/QGL/APS2CustomInstructions.py +++ b/QGL/APS2CustomInstructions.py @@ -49,13 +49,15 @@ def RandomCliffordSetOffset(addr, offset): return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, offset, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), # - CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0xA)] + CustomInstruction("APS_CLIFFORD_SET_OFFSET", addr, 0xA), + LoadCmpVram(0xA, 0xFFFFFFFF, tdm=False)] def RandomCliffordSetSpacing(addr, spacing): return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, spacing, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0xA)] + CustomInstruction("APS_CLIFFORD_SET_SPACING", addr, 0xA), + LoadCmpVram(0xA, 0xFFFFFFFF, tdm=False)] def RandomClifford(target, addr): return [Invalidate(addr, 0, tdm=False), @@ -73,10 +75,12 @@ def RandomCliffordInverseReset(val=0x0, addr=0xD): #for now don't use addr return [Invalidate(addr, 0, tdm=False), WriteAddr(addr, val, tdm=False), LoadCmpVram(addr, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_INVERSE_RESET", addr, 0xA)] + CustomInstruction("APS_CLIFFORD_INVERSE_RESET", addr, 0xA), + LoadCmpVram(0xA, 0xFFFFFFFF, tdm=False)] def RandomCliffordSeed(seed): return [Invalidate(0xB, 0, tdm=False), WriteAddr(0xB, seed, tdm=False), LoadCmpVram(0xB, 0xFFFFFFFF, tdm=False), - CustomInstruction("APS_CLIFFORD_SET_SEED", 0xB, 0xA)] + CustomInstruction("APS_CLIFFORD_SET_SEED", 0xB, 0xA), + LoadCmpVram(0xA, 0xFFFFFFFF, tdm=False)] From fa544b83af70268a3fadb74bcd620ef2abebb998 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 29 Jul 2019 13:37:27 -0400 Subject: [PATCH 229/235] Add explicit random inverse pulse. --- QGL/Compiler.py | 2 ++ QGL/RandomCliffordTools.py | 26 +++++++++++++++----------- 2 files changed, 17 insertions(+), 11 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index a104e066..c41e64f1 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -249,6 +249,8 @@ def get_clifford_type(wire, allowed_types=VALID_CLIFFORD_TYPES): if len(rt_pulses) == 0: return None pulse_type = list(set([pulse.label for pulse in rt_pulses])) + if 'RandomInverse' in pulse_type: + pulse_type.remove('RandomInverse') pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) if len(pulse_type) > 1: raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index df848aa2..a494364f 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -35,7 +35,7 @@ default_clifford_options = {"offset": 0x0, "spacing": 0x1, "seed": 0x31} -VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomCliff') +VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomCliff', 'RandomTestCliff') def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" @@ -80,10 +80,14 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, new_seq = [] for pulse in seq: - if isinstance(pulse, Pulse) and pulse.isRunTime \ - and pulse.label in VALID_CLIFFORD_TYPES: - has_random_cliff = True - new_seq.extend(RandomClifford(jt_label, cliff_addr)) + if isinstance(pulse, Pulse) and pulse.isRunTime: + if pulse.label in VALID_CLIFFORD_TYPES: + has_random_cliff = True + new_seq.extend(RandomClifford(jt_label, cliff_addr)) + elif pulse.label == 'RandomInverse': + new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) + else: + raise Exception(f"Unhandled run-time pulse: {pulse.label}") #print("Inserting clifford pulse!") else: new_seq.append(pulse) @@ -94,14 +98,14 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, # new_seq[0:0] = info_seqs if add_inv: - #insert reset after first wait + #insert reset before first wait w_idx = next(i for i, v in enumerate(new_seq) if isinstance(v, ControlFlow.Wait)) - new_seq.insert(w_idx+1, RandomCliffordInverseReset(0x0)) + new_seq.insert(w_idx, RandomCliffordInverseReset(0x0)) #insert at end of sequence or before last GOTO - if isinstance(new_seq[-1], ControlFlow.Goto): - new_seq[-1:-1] = RandomCliffordInverse(jt_label, inv_addr) - else: - new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) + #if isinstance(new_seq[-1], ControlFlow.Goto): + # new_seq[-1:-1] = RandomCliffordInverse(jt_label, inv_addr) + #else: + # new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) seqs[idx] = new_seq From 751345142bc48854f92eb47875536027e0ad886b Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 29 Jul 2019 13:38:42 -0400 Subject: [PATCH 230/235] Add TestClifford pulse for testing randomizer output. --- QGL/PulsePrimitives.py | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 5627d6d3..4efd5f6c 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -352,9 +352,23 @@ def RandomDiAC(qubit): params["length"] *= 2.0 return Pulse("RandomDiAC", qubit, params, isRunTime=True) +def RandomInverse(qubit): + params = overrideDefaults(qubit, {}) + return Pulse("RandomInverse", qubit, params, isRunTime=True) + from . import Cliffords -def Clifford(qubit, cliffNum): +def RandomTestCliff(qubit): + params = overrideDefaults(qubit, {}) + return Pulse("RandomTestCliff", qubit, params, isRunTime=True) + +def TestCliff(qubit, cliffNum): + params = overrideDefaults(qubit, {'shape_fun': 'constant'}) + amp = cliffNum * (1.0/24.0) + (1.0/24.0) + return Pulse("TCPulse", qubit, params, amp, 0.0, 0.0, frequency=0.0) + + +def Cliff(qubit, cliffNum): return reduce(operator.add, [p for p in Cliffords.clifford_seq(cliffNum, qubit)]) def AC(qubit, cliffNum): From ab5b379f9daa9736e66714f25cfc8e0593b9c37c Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Mon, 29 Jul 2019 13:39:15 -0400 Subject: [PATCH 231/235] Restore sequence file viewer code that got nuked at some point --- QGL/drivers/APS2Pattern.py | 123 ++++++++++++++++++++++++++++++++++++- 1 file changed, 122 insertions(+), 1 deletion(-) diff --git a/QGL/drivers/APS2Pattern.py b/QGL/drivers/APS2Pattern.py index 669f4563..6ece1645 100644 --- a/QGL/drivers/APS2Pattern.py +++ b/QGL/drivers/APS2Pattern.py @@ -98,7 +98,7 @@ # Whether we use PHASE_OFFSET modulation commands or bake it into waveform # Default to false as we usually don't have many variants -USE_PHASE_OFFSET_INSTRUCTION = True +USE_PHASE_OFFSET_INSTRUCTION = False # Whether to save the waveform offsets for partial compilation SAVE_WF_OFFSETS = False @@ -1557,3 +1557,124 @@ def display_raw_instructions(raw): def display_raw_file(filename): raw = raw_instructions(filename) display_raw_instructions(raw) + +if __name__ == '__main__': + if len(sys.argv) == 2: + + from PyQt5.QtWidgets import QMainWindow, QApplication, QWidget, QTableWidget, QTableWidgetItem, QVBoxLayout, QAbstractItemView, QPushButton + from PyQt5.QtGui import QIcon, QColor, QFont + + from matplotlib.backends.backend_qt4agg import FigureCanvasQTAgg + from matplotlib.figure import Figure + + table_font = QFont("Arial", weight=QFont.Bold) + + colors = {"WFM": QColor(0,200,0), + "GOTO": QColor(0,100,100), + "MARKER": QColor(150,150,200)} + + class MatplotlibWidget(QWidget): + def __init__(self, I, Q, parent=None): + super(MatplotlibWidget, self).__init__(parent) + self.title = 'Waveform' + self.left = 100 + self.top = 100 + self.width = 800 + self.height = 600 + self.setWindowTitle(self.title) + self.setGeometry(self.left, self.top, self.width, self.height) + + self.figure = Figure() + self.canvas = FigureCanvasQTAgg(self.figure) + + self.axis = self.figure.add_subplot(111) + self.axis.plot(I) + self.axis.plot(Q) + self.layout = QVBoxLayout(self) + self.layout.addWidget(self.canvas) + self.setLayout(self.layout) + self.canvas.draw() + self.show() + + + class App(QWidget): + + COLUMN_COUNT = 7 + def __init__(self, instructions, waveforms): + super().__init__() + self.title = 'APS2 Disassembled Instructions' + self.left = 100 + self.top = 100 + self.width = 1000 + self.height = 1200 + self.instructions = instructions + self.waveforms = waveforms + print(self.waveforms) + self.initUI() + self.plotters = [] + + def initUI(self): + self.setWindowTitle(self.title) + self.setGeometry(self.left, self.top, self.width, self.height) + + self.createTable() + self.layout = QVBoxLayout() + self.layout.addWidget(self.tableWidget) + self.setLayout(self.layout) + + # Show widget + self.show() + + def createTable(self): + # Create table + self.tableWidget = QTableWidget() + self.tableWidget.setRowCount(len(self.instructions)) + self.tableWidget.setColumnCount(7) + + for k, instr in enumerate(self.instructions): + fields = str(instr).replace(',','').replace(';', '').split(" ") + if "|" in fields: + fields.remove("|") + if fields[0] in colors: + color = colors[fields[0]] + else: + color = None + for l, f in enumerate(fields): + text = fields[l] + if text == "GOTO": + btn = QPushButton(self.tableWidget) + btn.setText('GOTO') + target_row = int(fields[1].split("=")[1]) + def scroll_to_goto_target(row=target_row, tab=self.tableWidget): + tab.scrollToItem(tab.item(row, 0)) + btn.clicked.connect(scroll_to_goto_target) + self.tableWidget.setCellWidget(k, l, btn) + if text == "WFM" and int(fields[4].split("=")[1])==0: + # Not a TA pair + btn = QPushButton(self.tableWidget) + btn.setText('WFM') + addr = int(fields[6].split("=")[1]) + count = int(fields[5].split("=")[1]) + def open_plotter(addr=None, I=self.waveforms[0][addr:addr+count], Q=self.waveforms[1][addr:addr+count]): + w = MatplotlibWidget(I,Q) + self.plotters.append(w) + btn.clicked.connect(open_plotter) + self.tableWidget.setCellWidget(k, l, btn) + else: + item = QTableWidgetItem(text) + item.setFont(table_font) + if color: + item.setBackground(color) + self.tableWidget.setItem(k, l, item) + if l < self.COLUMN_COUNT-1: + for j in range(l+1, self.COLUMN_COUNT): + item = QTableWidgetItem("") + if color: + item.setBackground(color) + self.tableWidget.setItem(k, j, item) + self.tableWidget.move(0,0) + self.tableWidget.setSelectionBehavior(QAbstractItemView.SelectRows) + + app = QApplication(sys.argv[:1]) + ex = App(read_instructions(sys.argv[1]), read_waveforms(sys.argv[1])) + sys.exit(app.exec_()) From 1cdf53b4b4f6e5163a16cc7cc00e8569158f47e4 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 29 Jul 2019 13:59:49 -0400 Subject: [PATCH 232/235] Add ability to reset inverse tracker to other than identity. --- QGL/Compiler.py | 7 +++++-- QGL/PulsePrimitives.py | 22 +++++++++++++++++++++- QGL/RandomCliffordTools.py | 18 +++++------------- 3 files changed, 31 insertions(+), 16 deletions(-) diff --git a/QGL/Compiler.py b/QGL/Compiler.py index c41e64f1..f6538f88 100644 --- a/QGL/Compiler.py +++ b/QGL/Compiler.py @@ -249,8 +249,11 @@ def get_clifford_type(wire, allowed_types=VALID_CLIFFORD_TYPES): if len(rt_pulses) == 0: return None pulse_type = list(set([pulse.label for pulse in rt_pulses])) - if 'RandomInverse' in pulse_type: - pulse_type.remove('RandomInverse') + #Remove pulses that are not the actual cliffords we want to play. + #We assume the RandomInverse is of the same type as the RandomCliffords! + for p in ('RandomInverse', 'RandomInverseReset'): + if p in pulse_type: + pulse_type.remove(p) pulse_logical_chan = list(set([pulse.channel for pulse in rt_pulses])) if len(pulse_type) > 1: raise Exception(f"All run-time pulses must have the same label. Found: {pulse_type}.") diff --git a/QGL/PulsePrimitives.py b/QGL/PulsePrimitives.py index 4efd5f6c..b9d66f2a 100644 --- a/QGL/PulsePrimitives.py +++ b/QGL/PulsePrimitives.py @@ -340,22 +340,43 @@ def arb_axis_drag(qubit, params, 1.0, aziAngle, frameChange) def RandomCliff(qubit): + """A randomly generated clifford gate, using the standard clifford library. + """ params = overrideDefaults(qubit, {}) return Pulse("RandomCliff", qubit, params, isRunTime=True) def RandomAC(qubit): + """A randomly generated clifford gate, using the diatomic clifford library. + """ params = overrideDefaults(qubit, {}) return Pulse("RandomAC", qubit, params, isRunTime=True) def RandomDiAC(qubit): + """A randomly generated clifford gate, using the atomic clifford library. + """ params = overrideDefaults(qubit, {}) params["length"] *= 2.0 return Pulse("RandomDiAC", qubit, params, isRunTime=True) def RandomInverse(qubit): + """The currently-stored `Inverse` clifford generated by the on-board frame + tracking hardware. + """ params = overrideDefaults(qubit, {}) return Pulse("RandomInverse", qubit, params, isRunTime=True) +def RandomInverseReset(qubit, reset_value=0): + """Reset the on-board frame tracker to the given value (clifford index). + NOTE: Because of potential timing issues with the random clifford firmware, + this "pulse" will be played in the "dead time" between sequences (i.e. + BEFORE the first WAIT of a sequence.) No guarantees as to what will happen if + you try to put this pulse inside of a sequence... + """ + params = overrideDefaults(qubit, {}) + #note that we store the reset value in the `maddr` paramter, otherwise unused + #TODO: this is a bit hacky and should probably be changed. + return Pulse("RandomInverseReset", qubit, params, isRunTime=True, maddr=reset_value) + from . import Cliffords def RandomTestCliff(qubit): @@ -367,7 +388,6 @@ def TestCliff(qubit, cliffNum): amp = cliffNum * (1.0/24.0) + (1.0/24.0) return Pulse("TCPulse", qubit, params, amp, 0.0, 0.0, frequency=0.0) - def Cliff(qubit, cliffNum): return reduce(operator.add, [p for p in Cliffords.clifford_seq(cliffNum, qubit)]) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index a494364f..8fae9f73 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -78,6 +78,7 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, continue new_seq = [] + inverse_val = 0 for pulse in seq: if isinstance(pulse, Pulse) and pulse.isRunTime: @@ -86,29 +87,20 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, new_seq.extend(RandomClifford(jt_label, cliff_addr)) elif pulse.label == 'RandomInverse': new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) + elif pulse.label == 'RandomInverseReset': + inverse_val = pulse.maddr else: raise Exception(f"Unhandled run-time pulse: {pulse.label}") #print("Inserting clifford pulse!") else: new_seq.append(pulse) - # if isinstance(seq[0], BlockLabel.BlockLabel): - # new_seq[1:1] = info_seqs - # else: - # new_seq[0:0] = info_seqs - if add_inv: - #insert reset before first wait w_idx = next(i for i, v in enumerate(new_seq) if isinstance(v, ControlFlow.Wait)) - new_seq.insert(w_idx, RandomCliffordInverseReset(0x0)) - #insert at end of sequence or before last GOTO - #if isinstance(new_seq[-1], ControlFlow.Goto): - # new_seq[-1:-1] = RandomCliffordInverse(jt_label, inv_addr) - #else: - # new_seq.extend(RandomCliffordInverse(jt_label, inv_addr)) - + new_seq.insert(w_idx, RandomCliffordInverseReset(val=inverse_val)) seqs[idx] = new_seq + #Helper "sequence" to set the random clifford settings. info_seqs = [] info_seqs.extend(RandomCliffordSetOffset(0x1, clifford_options["offset"])) info_seqs.extend(RandomCliffordSetSpacing(0x2, clifford_options["spacing"])) From 602dd1f819466d7818332104b9571602aa77bcd7 Mon Sep 17 00:00:00 2001 From: gribeill Date: Mon, 29 Jul 2019 14:18:17 -0400 Subject: [PATCH 233/235] Update random clifford sequence builder --- QGL/RandomCliffordTools.py | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index 8fae9f73..ebcfcfaa 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -108,8 +108,14 @@ def insert_clifford_calls(seqs, jt_label=None, cliff_addr=0x3, add_inv = True, seqs.insert(0, info_seqs) -def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomAC): - - c_seqs = [[clifford_type(qubit) for _ in seq] for seq in seqs] - inverses = [inverse_clifford(C1[reduce(clifford_multiply, seq)]) for seq in seqs] - return c_seqs, inverses +def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.RandomCliff): + """Randomize a sequence of cliffords (given as integers). + """ + rseqs = [] + for seq in seqs: + inverse = reduce(clifford_multiply, seq) + rseq = ([PulsePrimitives.RandomCliffordInverse(qubit, reset_value=inverse)] + + [clifford_type(qubit)]*len(seq) + + [RandomInverse(qubit), MEAS(qubit)]) + rseqs.append(rseq) + return rseqs From eacf5beb9175c6bc63678079af40fbd8cddf6fd3 Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Thu, 25 Jul 2019 13:43:33 -0400 Subject: [PATCH 234/235] Modify GST code for newest pygsti stuff. --- QGL/GSTTools.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/QGL/GSTTools.py b/QGL/GSTTools.py index 0052b1c6..49ad9749 100644 --- a/QGL/GSTTools.py +++ b/QGL/GSTTools.py @@ -28,7 +28,7 @@ PYGSTI_PRESENT = False try: - from pygsti.objects import GateString + from pygsti.objects.circuit import Circuit PYGSTI_PRESENT = True except: pass @@ -51,11 +51,11 @@ def gst_map_1Q(gst_list, qubit, qgl_map=gst_gate_map, append_meas=True): Returns: QGL sequences, preserving the input list nesting (as a generator) """ - if isinstance(gst_list, GateString): + if isinstance(gst_list, Circuit): gst_list = [gst_list] for item in gst_list: - if isinstance(item, GateString): - mapped = map(lambda x: qgl_map[x](qubit), item.tup) + if isinstance(item, Circuit): + mapped = map(lambda x: qgl_map[str(x)](qubit), item.tup) if append_meas: yield list(chain(mapped, [MEAS(qubit)])) else: From 242118d6f680de457a2ca79672895b10d211854e Mon Sep 17 00:00:00 2001 From: Guilhem Ribeill Date: Thu, 29 Aug 2019 17:04:32 -0400 Subject: [PATCH 235/235] Support Random diatomic pulses --- QGL/RandomCliffordTools.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/QGL/RandomCliffordTools.py b/QGL/RandomCliffordTools.py index ebcfcfaa..1f61c34e 100644 --- a/QGL/RandomCliffordTools.py +++ b/QGL/RandomCliffordTools.py @@ -35,7 +35,7 @@ default_clifford_options = {"offset": 0x0, "spacing": 0x1, "seed": 0x31} -VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomCliff', 'RandomTestCliff') +VALID_CLIFFORD_TYPES = ('RandomAC', 'RandomCliff', 'RandomTestCliff', 'RandomDiAC') def generate_clifford_jump_table(cliff_wires, jt_label = None): """Generate the jump table that will be used to call into the clifford set""" @@ -114,7 +114,7 @@ def randomize_clifford_sequences(qubit, seqs, clifford_type=PulsePrimitives.Rand rseqs = [] for seq in seqs: inverse = reduce(clifford_multiply, seq) - rseq = ([PulsePrimitives.RandomCliffordInverse(qubit, reset_value=inverse)] + + rseq = ([PulsePrimitives.RandomInverse(qubit, reset_value=inverse)] + [clifford_type(qubit)]*len(seq) + [RandomInverse(qubit), MEAS(qubit)]) rseqs.append(rseq)