diff --git a/.gitmodules b/.gitmodules index aa85eb4..b181281 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,4 +1,4 @@ [submodule "QuEST"] path = QuEST url = https://github.com/QuEST-Kit/QuEST - branch = develop + branch = temp-questlink-submodule diff --git a/Link/QuESTlink.m b/Link/QuESTlink.m index 345db80..368ff25 100644 --- a/Link/QuESTlink.m +++ b/Link/QuESTlink.m @@ -578,6 +578,9 @@ The sum of the expected values of the (potentially unnormalised) state-vectors o Damp::usage = "Damp[prob] is 1 qubit amplitude damping with the given decay probability." Protect[Damp] + PauliError::usage = "PauliError[pX,pY,pZ] is a 1 qubit inhomogeneous depolarising channel, where pX, pY, pZ are the probabilities of X, Y and Z errors respectively." + Protect[PauliError] + SWAP::usage = "SWAP is a 2 qubit gate which swaps the state of two qubits." Protect[SWAP] @@ -780,7 +783,7 @@ The probability of the forced measurement outcome (as if it were hypothetically (* opcodes which correlate with the global IDs in circuits.hpp *) getOpCode[gate_] := - gate /. {H->0,X->1,Y->2,Z->3,Rx->4,Ry->5,Rz->6,R->7,S->8,T->9,U->10,Deph->11,Depol->12,Damp->13,SWAP->14,M->15,P->16,Kraus->17,G->18,Id->19,Ph->20,KrausNonTP->21,Matr->22,UNonNorm->23,Fac->24,_->-1} + gate /. {H->0,X->1,Y->2,Z->3,Rx->4,Ry->5,Rz->6,R->7,S->8,T->9,U->10,Deph->11,Depol->12,Damp->13,SWAP->14,M->15,P->16,Kraus->17,G->18,Id->19,Ph->20,KrausNonTP->21,Matr->22,UNonNorm->23,Fac->24,PauliError->25,_->-1} @@ -953,7 +956,7 @@ The probability of the forced measurement outcome (as if it were hypothetically circContainsDecoherence[circuit_List] := MemberQ[ circuit, - Subscript[ Damp | Deph | Depol | Kraus | KrausNonTP, __ ][__]] + Subscript[ Damp | Deph | Depol | Kraus | KrausNonTP | PauliError, __ ][__]] @@ -1028,6 +1031,7 @@ The probability of the forced measurement outcome (as if it were hypothetically *) encodeDerivParams[Subscript[Rx|Ry|Rz|Ph|Damp|Deph|Depol, __][f_], x_] := {D[f,x]} + encodeDerivParams[Subscript[PauliError, __][f__], x_] := D[{f},x] encodeDerivParams[R[f_,_], x_] := {D[f,x]} encodeDerivParams[G[f_], x_] := {D[f,x]} encodeDerivParams[Fac[f_], x_] := With[{df=D[f,x]}, {Re@N@df, Im@N@df}] @@ -1782,6 +1786,11 @@ The probability of the forced measurement outcome (as if it were hypothetically Circuit[ Fac@Sqrt[x/3] Subscript[X, q] ], Circuit[ Fac@Sqrt[x/3] Subscript[Y, q] ], Circuit[ Fac@Sqrt[x/3] Subscript[Z, q] ]} + convertOpToPureCircs[Subscript[PauliError, q_][px_,py_,pz_]] := { + Circuit[ Fac@Sqrt[1-(px+py+pz)] ], + Circuit[ Fac@Sqrt[px] Subscript[X, q] ], + Circuit[ Fac@Sqrt[py] Subscript[Y, q] ], + Circuit[ Fac@Sqrt[pz] Subscript[Z, q] ]} convertOpToPureCircs[Subscript[Depol, q1_,q2_][x_]] := Join[{{Fac@Sqrt[1-x]}}, Rest @ Flatten[ Table[{Fac@Sqrt[x/15], Subscript[a, q1], Subscript[b, q2]}, {a,{Id,X,Y,Z}}, {b,{Id,X,Y,Z}}] /. Subscript[Id, _] -> Nothing, 1]] @@ -1810,7 +1819,9 @@ The probability of the forced measurement outcome (as if it were hypothetically Subscript[Deph, _][x_ /; x < 0 || x > 1/2] | Subscript[Deph, _, _][x_ /; x < 0 || x > 3/4] | Subscript[Depol, _][x_ /; x < 0 || x > 3/4] | - Subscript[Depol, _, _][x_ /; x < 0 || x > 15/16]], + Subscript[Depol, _, _][x_ /; x < 0 || x > 15/16] + (Subscript[PauliError, _][x_, y_, z_] /; ( x<0 || y<0 || z<0 || x+y+z>1 || Max[x,y,z] > 1-(x+y+z) )) + ], Message[GetRandomCircuitFromChannel::error, "A unitary-mixture channel had an invalid probability which was negative or exceeded that causing maximal mixing."]; Return[$Failed]]; @@ -2494,11 +2505,16 @@ The probability of the forced measurement outcome (as if it were hypothetically drawGate[Ph, {ctrls___}, {targs__}, col_] := { drawControls[{ctrls,targs},{},col], Text["\[Theta]", {col+.75,Min[{ctrls,targs}]+.75}]} - drawGate[label:(Kraus|KrausNonTP|Damp|Deph|Depol), {}, targs_List, col_] := { + drawGate[label:(Kraus|KrausNonTP|Damp|Deph|Depol|PauliError), {}, targs_List, col_] := { EdgeForm[Dashed], drawGate[label /. { - Kraus -> \[Kappa], KrausNonTP -> \[Kappa]NTP, Damp -> \[Gamma], - Deph -> \[Phi], Depol -> \[CapitalDelta]}, + Kraus -> \[Kappa], + KrausNonTP -> \[Kappa]NTP, + Damp -> \[Gamma], + Deph -> \[Phi], + Depol -> \[CapitalDelta], + PauliError -> \[Sigma] + }, {}, targs, col]} (* single qubit gate graphics *) @@ -2516,9 +2532,6 @@ The probability of the forced measurement outcome (as if it were hypothetically Circle[{col+.5,targ+.5-.4}, .4, {.7,\[Pi]-.7}], Line[{{col+.5,targ+.5-.25}, {col+.5+.2,targ+.5+.3}}] }, {targ, {targs}}] - - drawGate[Depol, {}, {targ_}, col_] := { - EdgeForm[Dashed], drawGate[\[CapitalDelta], {}, {targ}, col]} drawGate[X, {}, {targ_}, col_] := { Circle[{col+.5,targ+.5},.25], Line[{{col+.5,targ+.5-.25},{col+.5,targ+.5+.25}}]} @@ -3103,6 +3116,11 @@ The probability of the forced measurement outcome (as if it were hypothetically Sqrt[p/3] PauliMatrix[1], Sqrt[p/3] PauliMatrix[2], Sqrt[p/3] PauliMatrix[3]}], + Subscript[PauliError, q_][px_,py_,pz_] :> Subscript[Kraus, q][{ + Sqrt[1-(px+py+pz)] PauliMatrix[0], + Sqrt[px] PauliMatrix[1], + Sqrt[py] PauliMatrix[2], + Sqrt[pz] PauliMatrix[3]}], Subscript[Depol, q1_,q2_][p_] :> Subscript[Kraus, q1,q2][ Join[ {Sqrt[1-p] IdentityMatrix[4]}, Flatten[ Table[ @@ -3190,8 +3208,8 @@ The probability of the forced measurement outcome (as if it were hypothetically (* Damp, Deph are entirely real (when valid), else we assume principal Sqrts, * and although Depol contains Y, its superoperator/application cancels conj(Y)=-Y *) - getGateConj[valid_] @ (g:Subscript[Damp|Deph|Depol, __]) @ x_ := - If[valid, g[x], g @ Conjugate @ x] + getGateConj[valid_] @ (g:Subscript[Damp|Deph|Depol|PauliError, __]) @ x__ := + If[valid, g[x], g @@ Conjugate @ {x}] (* unmatched operators *) getGateConj[valid_][g_] := Message[GetCircuitConjugated::error, @@ -3216,12 +3234,19 @@ The probability of the forced measurement outcome (as if it were hypothetically (* rules to simplify operators when AssertValidChannels -> True *) (* note we do not simplify/assert real-parameterised gates like rotations, out of laziness *) + (* beware we are simplifying under the constraints that the channels never surpass maximum mixing, + * which is in-fact distinct and stronger than constraining they are merely CPTP. *) assertReal[x_] := Element[x, Reals] getValidChannelAssumps @ Subscript[Damp, _][x_] := assertReal[x] && (0 <= x <= 1) getValidChannelAssumps @ Subscript[Deph, _][x_] := assertReal[x] && (0 <= x <= 1/2) getValidChannelAssumps @ Subscript[Deph, _,_][x_] := assertReal[x] && (0 <= x <= 3/4) getValidChannelAssumps @ Subscript[Depol, _][x_] := assertReal[x] && (0 <= x <= 3/4) getValidChannelAssumps @ Subscript[Depol, _,_][x_] := assertReal[x] && (0 <= x <= 15/16) + getValidChannelAssumps @ Subscript[PauliError, _][x_,y_,z_] := ( + assertReal[x] && (0 <= x <= 1) && x <= (1 - (x+y+z)) && + assertReal[y] && (0 <= y <= 1) && y <= (1 - (x+y+z)) && + assertReal[z] && (0 <= z <= 1) && z <= (1 - (x+y+z)) + ) (* un-targeted operators merge with their conjugate *) getSuperOpCirc[numQb_,valid_] @ G[x_] := @@ -3234,8 +3259,8 @@ The probability of the forced measurement outcome (as if it were hypothetically {supermatr = Total[(KroneckerProduct[Conjugate[#], #]&) /@ matrs]}, {Subscript[Matr, q, Sequence @@ ({q} + numQb)] @ supermatr}] - (* Damp, Depol, Deph must first be generalised to Kraus, then recursed upon... *) - getSuperOpCirc[numQb_,valid_] @ g:Subscript[Damp|Deph|Depol, q__][x_] := With[ + (* Damp, Depol, Deph, PauliError must first be generalised to Kraus, then recursed upon... *) + getSuperOpCirc[numQb_,valid_] @ g:Subscript[Damp|Deph|Depol|PauliError, q__][__] := With[ {supers = getSuperOpCirc[numQb, valid] /@ GetCircuitGeneralised @ g}, {assumps = getValidChannelAssumps @ g}, (* and the result is optionally simplified, if CPTP condition is possible (else caller Aborts) *) @@ -3387,7 +3412,7 @@ The probability of the forced measurement outcome (as if it were hypothetically " which is not supported by the given device specification. Note this may be due to preceding gates," <> " if the spec contains constraints which depend on dynamic variables. See ?GetUnsupportedGates."]; $Failed], - (* if match, but targeted qubits don't exist, throw top-level error *) + (* if match, but targeted qubits do not exist, throw top-level error *) Not @ AllTrue[gateQubits, LessThan[spec[NumAccessibleQubits]]], Throw[ Message[InsertCircuitNoise::error, "The gate " <> ToString@StandardForm@gate <> @@ -3437,11 +3462,11 @@ The probability of the forced measurement outcome (as if it were hypothetically Do[ With[ {qubitProps = Replace[qubit, spec[Qubits]]}, - (* continue only if qubit matches a rule in spec[Qubits] (don't noise unspecified qubits) *) + (* continue only if qubit matches a rule in spec[Qubits] (do not noise unspecified qubits) *) If[ qubitProps =!= qubit, With[ - (* work out start-time and duration of qubit's passive noise (pre-determined) *) + (* work out start-time and duration of passive noise of qubits (pre-determined) *) {passiveTime = subcircTime + qubitActiveDurs[[ 1 + qubit ]]}, {passiveDur = subcircDur - qubitActiveDurs[[ 1 + qubit ]]}, (* work out passive noise (time, dur and var dependent *) @@ -3474,7 +3499,7 @@ The probability of the forced measurement outcome (as if it were hypothetically spec[InitVariables][]]; Do[ - (* get each subcirc's noise (updates circuit variables) *) + (* get noise of each subcirc (updates circuit variables) *) {curDur, curActive, curPassive} = getDurAndNoiseFromSubcirc[sub, curTime, spec]; (* losing info here (mering separate gate infos into subcirc-wide) *) @@ -4061,7 +4086,7 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) Subscript[C, c__Integer|{c__Integer}][g_] :> Subscript[C, tidyInds@c][g], (* sort targets of target-order-agnostic gates *) Subscript[(g:(H|X|Y|Z|Id|SWAP|Ph|M||T|S)), t__Integer|{t__Integer}] :> Subscript[g, tidyInds@t], - Subscript[(g:(Rx|Ry|Rz|Damp|Deph|Depol)), t__Integer|{t__Integer}][x__] :> Subscript[g, tidyInds@t][x], + Subscript[(g:(Rx|Ry|Rz|Damp|Deph|Depol|PauliError)), t__Integer|{t__Integer}][x__] :> Subscript[g, tidyInds@t][x], (* unpack all qubit lists *) Subscript[s_, {t__}] :> Subscript[s, t], Subscript[s_, {t__}][x_] :> Subscript[s, t][x], @@ -4162,6 +4187,7 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) Subscript[C, __][Nothing] -> Nothing, (* remove zero-parameter gates *) Subscript[(Ph|Rx|Ry|Rz|Damp|Deph|Depol), __][0|0.] -> Nothing, + Subscript[PauliError, __][0|0., 0|0., 0|0.] -> Nothing, R[0|0.,_] -> Nothing, G[0|0.] -> Nothing, Fac[1|1.] -> Nothing, @@ -4391,7 +4417,7 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) Subscript[C, retargetQubits[c, map]] @ retargetGate[map] @ g (* avoid modifying arg of parameterised gates *) - retargetGate[map_][ Subscript[s:Damp|Deph|Depol|Kraus|KrausNonTP|Matr|P|Ph|Rx|Ry|Rz|U|UNonNorm, t__][args__] ] := + retargetGate[map_][ Subscript[s:Damp|Deph|Depol|PauliError|Kraus|KrausNonTP|Matr|P|Ph|Rx|Ry|Rz|U|UNonNorm, t__][args__] ] := Subscript[s, retargetQubits[t, map]] @ args (* modify only the qubits of the Pauli product in R gates *) @@ -4484,7 +4510,7 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) getGateParameterised[r_, _, exclParamsPatt_, g_ ] /; isParamInExcludeList[exclParamsPatt, g] := {g, None} - (* re-paramaterise non-controlled gates *) + (* re-paramaterise non-controlled one-parameter gates *) getGateParameterised[r_, _,_, (g:Subscript[Damp|Deph|Depol|Ph|Rx|Ry|Rz, __])[x_]] := {g[r], x} getGateParameterised[r_, _,_, (g:Fac|G)[x_]] := @@ -4492,6 +4518,10 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) getGateParameterised[r_, _,_, R[x_, p_]] := {R[r,p], x} + (* re-parameterise non-controlled multi-parameter gates (to a list) *) + getGateParameterised[s_[i_], _,_, (g:Subscript[PauliError, __])[px_,py_,pz_]] := + {g[s[i], s[i+1], s[i+2]], {px,py,pz}} + (* re-param control gates, removing excludes *) getGateParameterised[r_, _, exclParamsPatt_, (c:Subscript[C,__])[g_] ] /; Not @ isParamInExcludeList[exclParamsPatt, g] := With[ @@ -4502,11 +4532,11 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) getGateParameterised[r_, _,_, g_] := {g, None} - GetCircuitParameterised[gate_?isGateFormat, s_Symbol, opts:OptionsPattern[]] := - GetCircuitParameterised[{gate}, s, opts] + GetCircuitParameterised[gate_?isGateFormat, paramsym_Symbol, opts:OptionsPattern[]] := + GetCircuitParameterised[{gate}, paramsym, opts] - GetCircuitParameterised[circuit_?isCircuitFormat, s_Symbol, OptionsPattern[]] := Module[ - {exclGatesPatt,exclParamsPatt, paramInd,outGate,paramVal, outCircuit,outParams, newSymb,newParams,symbSubs}, + GetCircuitParameterised[circuit_?isCircuitFormat, paramsym_Symbol, OptionsPattern[]] := Module[ + {exclGatesPatt,exclParamsPatt, paramInd,outGate,paramValOrVals, outCircuit,outParams, newSymb,newParams,symbSubs}, (* validate all options are recognised *) Check[ OptionValue @ "ExcludeChannels", Return @ $Failed]; @@ -4525,19 +4555,22 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) If[ Head @ exclParamsPatt === List, exclParamsPatt = Alternatives @@ exclParamsPatt]; If[ OptionValue @ "ExcludeChannels", - exclGatesPatt = exclGatesPatt | Subscript[Damp|Deph|Depol,__][_]]; + exclGatesPatt = exclGatesPatt | Subscript[Damp|Deph|Depol|PauliError,__][__]]; paramInd = 1; outCircuit = {}; outParams = {}; Do[ (* conditionally insert symbolic param from each gate... *) - {outGate, paramVal} = getGateParameterised[ - s[paramInd], exclGatesPatt, exclParamsPatt, inGate]; + {outGate, paramValOrVals} = getGateParameterised[ + paramsym[paramInd], exclGatesPatt, exclParamsPatt, inGate]; AppendTo[outCircuit, outGate]; - (* and if performed, increment the symbolic param counter *) - If[paramVal =!= None, AppendTo[outParams, s[paramInd++] -> paramVal]], + (* and if performed, increment the symbolic param counter for each value *) + If[paramValOrVals =!= None, + Do[ + AppendTo[outParams, paramsym[paramInd++] -> val], + {val, If[ListQ@paramValOrVals, paramValOrVals, {paramValOrVals}]}]], {inGate, circuit}]; paramInd = 1; @@ -4562,8 +4595,8 @@ variable renaming in nested scoping structs (Module[ Function[]]) *) outCircuit = outCircuit /. Flatten @ symbSubs; (* re-param circuit and subs back to user symbol*) - outCircuit = outCircuit /. TEMPSYMBOL[i_] :> s[i]; - outParams = newParams /. TEMPSYMBOL[i_] :> s[i]; + outCircuit = outCircuit /. TEMPSYMBOL[i_] :> paramsym[i]; + outParams = newParams /. TEMPSYMBOL[i_] :> paramsym[i]; ]; {outCircuit, outParams} diff --git a/Link/circuits.cpp b/Link/circuits.cpp index f7e36c7..c4aa9e2 100644 --- a/Link/circuits.cpp +++ b/Link/circuits.cpp @@ -196,6 +196,7 @@ std::string Gate::getName() { case OPCODE_Deph : case OPCODE_Depol : case OPCODE_Damp : + case OPCODE_PauliError : case OPCODE_Kraus : case OPCODE_KrausNonTP : if (numTargs == 0) @@ -488,6 +489,15 @@ void Gate::validate() { if (numTargs != 1) throw local_wrongNumGateTargsExcep(getSyntax(), getSymb(), numTargs, 1); // throws return; + + case OPCODE_PauliError : + if (numParams != 3) + throw local_wrongNumGateParamsExcep(getSyntax(), getSymb(), numParams, 3); // throws + if (numCtrls != 0) + throw local_gateUnsupportedExcep(getSyntax(), getName()); // throws + if (numTargs != 1) + throw local_wrongNumGateTargsExcep(getSyntax(), getSymb(), numTargs, 1); // throws + return; case OPCODE_SWAP : if (numParams != 0) @@ -736,6 +746,12 @@ void Gate::applyTo(Qureg qureg, qreal* outputs) { if (numTargs == 2) mixTwoQubitDepolarising(qureg, targs[0], targs[1], params[0]); // throws break; + + case OPCODE_PauliError : + if (params[0] == 0 && params[1] == 0 && params[2] == 0) + break; // permit zero-prob decoherence to act on state-vectors + mixPauli(qureg, targs[0], params[0], params[1], params[2]); // throws + break; case OPCODE_Damp : if (params[0] == 0) @@ -889,6 +905,7 @@ void Gate::applyDaggerTo(Qureg qureg) { case OPCODE_Z : case OPCODE_Deph : case OPCODE_Depol : + case OPCODE_PauliError : applyTo(qureg); // throws break; @@ -1014,6 +1031,7 @@ bool Gate::isUnitary() { case OPCODE_Deph : case OPCODE_Depol : case OPCODE_Damp : + case OPCODE_PauliError : case OPCODE_Kraus : case OPCODE_KrausNonTP : case OPCODE_Fac : @@ -1039,6 +1057,7 @@ bool Gate::isPure() { case OPCODE_Deph : case OPCODE_Depol : + case OPCODE_PauliError : case OPCODE_Damp : case OPCODE_Kraus : case OPCODE_KrausNonTP : @@ -1081,6 +1100,12 @@ bool Gate::isInvertible() { if (numTargs == 2) return local_isNonZero(15 - 16*params[0]); + case OPCODE_PauliError : + return local_isNonZero( + (1 - 2*params[1] - 2*params[2]) * + (1 - 2*params[0] - 2*params[2]) * + (1 - 2*params[0] - 2*params[1])); + case OPCODE_Damp : if (numTargs == 1) return local_isNonZero(1 - params[0]); @@ -1116,6 +1141,7 @@ bool Gate::isTracePreserving() { case OPCODE_Damp : case OPCODE_Deph : case OPCODE_Depol : + case OPCODE_PauliError : case OPCODE_Kraus : return true; @@ -1200,12 +1226,30 @@ void Gate::applyInverseTo(Qureg qureg) { densmatr_mixTwoQubitDepolarising(qureg, targs[0], targs[1], (16*invParam)/15.); } return; + + case OPCODE_PauliError : { ; + qreal oldX = params[0]; + qreal oldY = params[1]; + qreal oldZ = params[2]; + + qreal termX = 1 / (1 - 2*oldY - 2*oldZ); // Gate::isInvertible assures these won't divide-by-zero + qreal termY = 1 / (1 - 2*oldX - 2*oldZ); + qreal termZ = 1 / (1 - 2*oldX - 2*oldY); + + qreal newI = (1 + termX + termY + termZ) / 4; + qreal newX = (1 + termX - termY - termZ) / 4; + qreal newY = (1 + termX - termY + termZ * 2 * (oldX + oldY)) / 4; + qreal newZ = oldZ + 2 * (oldX + oldY - 1) * (oldX + oldZ) * (oldY + oldZ); + newZ /= - termX * termY * termZ; + + densmatr_mixArbitraryPauli(qureg, targs[0], newI, newX, newY, newZ); + } + return; - case OPCODE_Damp : - if (numTargs == 1) { - qreal invParam = params[0] / (params[0] - 1); - densmatr_mixDamping(qureg, targs[0], invParam); - } + case OPCODE_Damp : { ; + qreal invParam = params[0] / (params[0] - 1); + densmatr_mixDamping(qureg, targs[0], invParam); + } return; case OPCODE_Kraus : @@ -1248,6 +1292,9 @@ int Gate::getNumDecomps() { return 4; if (numTargs == 2) return 16; + + case OPCODE_PauliError : + return 4; case OPCODE_Kraus : case OPCODE_KrausNonTP : @@ -1397,6 +1444,36 @@ qreal Gate::applyDecompTo(Qureg qureg, int decompInd) { } break; + case OPCODE_PauliError : { ; + qreal pX = params[0]; + qreal pY = params[1]; + qreal pZ = params[2]; + qreal pI = 1 - (pX + pY + pZ); + + if (pX < 0 || pY < 0 || pZ < 0) + throw local_invalidProbExcep(getName(), pX, pY, pZ); // throws + + qreal pMax = 0; + if (pX > pMax) pMax = pX; + if (pY > pMax) pMax = pY; + if (pZ > pMax) pMax = pZ; + if (pMax > pI) + throw local_invalidProbExcep(getName(), pX, pY, pZ); // throws + + // apply I, X, Y or Z + qreal probs[] = {pI, pX, pY, pZ}; + int r = (isRand)? local_getRandomIndex(probs, 4) : decompInd; + if (r == 1) + pauliX(qureg, targs[0]); // throws + if (r == 2) + pauliY(qureg, targs[0]); // throws + if (r == 3) + pauliZ(qureg, targs[0]); // throws + + return probs[r]; + } + break; + case OPCODE_Kraus : case OPCODE_KrausNonTP : { ; int numOps = (int) params[0]; diff --git a/Link/circuits.hpp b/Link/circuits.hpp index 6387761..8a6758f 100644 --- a/Link/circuits.hpp +++ b/Link/circuits.hpp @@ -38,8 +38,9 @@ #define OPCODE_Matr 22 #define OPCODE_UNonNorm 23 #define OPCODE_Fac 24 +#define OPCODE_PauliError 25 -#define NUM_OPCODES 25 +#define NUM_OPCODES 26 static const std::string opcodeStrings[] = { "H", // OPCODE_H 0 @@ -67,6 +68,7 @@ static const std::string opcodeStrings[] = { "Matr", // OPCODE_Matr 22 "UNonNorm", // OPCODE_UNonNorm 23 "Fac", // OPCODE_Fac 24 + "PauliError", // OPCODE_PauliError 25 }; static const std::string opcodeNames[] = { @@ -95,6 +97,7 @@ static const std::string opcodeNames[] = { "general matrix", // OPCODE_Matr 22 "unnormalised general unitary", // OPCODE_UNonNorm 23 "factor", // OPCODE_Fac 24 + "inhomogeneous Pauli error", // OPCODE_PauliError 25 }; diff --git a/Link/derivatives.cpp b/Link/derivatives.cpp index 4d3b958..da91108 100644 --- a/Link/derivatives.cpp +++ b/Link/derivatives.cpp @@ -339,6 +339,10 @@ void Gate::applyDerivTo(Qureg qureg, qreal* derivParams, int numDerivParams) { if (numTargs == 2) extension_mixTwoQubitDepolarisingDeriv(qureg, targs[0], targs[1], derivParams[0]); // throws break; + + case OPCODE_PauliError : + throw QuESTException("", "Derivatives of operator " + getSymb() + " have not yet been implemented."); // throws, caught by below + break; case OPCODE_Damp : if (numDerivParams != 1) diff --git a/Link/errors.cpp b/Link/errors.cpp index a44bc96..37d020b 100644 --- a/Link/errors.cpp +++ b/Link/errors.cpp @@ -155,6 +155,19 @@ QuESTException local_invalidProbExcep(std::string gateName, qreal prob, std::str " for which " + gateName + " is maximally mixing."); // throws } +QuESTException local_invalidProbExcep(std::string gateName, qreal probX, qreal probY, qreal probZ) { + + if (probX < 0 || probY < 0 || probZ < 0) + return QuESTException("", "An error probability is negative and ergo invalid."); + if (probX > 1 || probY > 1 || probZ > 1) + return QuESTException("", "An error probability exceeded one and is ergo invalid."); + + if (probX + probY + probZ > 1) + return QuESTException("", "The error probabilities together exceeded one and are ergo invalid."); + + return QuESTException("", "The error probabilities together exceeded those which induce maximal mixing."); +} + void local_throwExcepIfUserAborted() { /* Dear ancient Wolfram Gods; why does this no longer work? diff --git a/Link/errors.hpp b/Link/errors.hpp index 05c8f49..c137375 100644 --- a/Link/errors.hpp +++ b/Link/errors.hpp @@ -46,6 +46,7 @@ QuESTException local_wrongNumDerivParamsExcep(std::string gate, int wrongNumPara QuESTException local_unrecognisedGateExcep(std::string gateSyntax, int opcode, const char* caller); QuESTException local_invalidProbExcep(std::string gateName, qreal prob, std::string maxProb); +QuESTException local_invalidProbExcep(std::string gateName, qreal probX, qreal probY, qreal probZ); void sendDebugEcho(std::string msg); diff --git a/QuEST b/QuEST index ce6bded..a30025b 160000 --- a/QuEST +++ b/QuEST @@ -1 +1 @@ -Subproject commit ce6bdedfa07a22df8ee29424d9f0f62160833b2f +Subproject commit a30025b6cf28b8dde2128b4b6d81414f84ee705a