├── Calc4ParticleSRV.py ├── CalcSRV.py ├── LICENSE ├── README.md ├── SRVCaseCheck.py └── SimpleHOMExample.py /Calc4ParticleSRV.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 1 15:36:08 2020 4 | 5 | @author: XuemeiGu 6 | email: njuxmgu@gmail.com 7 | 8 | """ 9 | 10 | ##### import packages 11 | import numpy as np 12 | import sympy as sp 13 | import random 14 | from numpy.random import choice 15 | from sympy.matrices import SparseMatrix 16 | from sympy import collect, expand, Symbol,sqrt,pi,I 17 | from itertools import combinations, chain 18 | 19 | FF1, FF2, FF3, FF4,FF5, HH, GG1, GG2, GG3, GG4,GG5=map(sp.IndexedBase,['FF1','FF2','FF3','FF4','FF5','HH','GG1','GG2','GG3','GG4','GG5']) 20 | l,l1, l2, l3, l4, l5, l6, l7, l8,coeff=map(sp.Wild,['l','l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8','coeff']) 21 | 22 | zero=Symbol('zero') ## using in 4-fold coincidence 23 | psi=Symbol('psi') ## represent a quantum state 24 | 25 | ## corresponding symbolic number 26 | sqr2=sqrt(2)/2 ## equals to 1/sqrt(2) 27 | imagI=I ## equals to Imaginary 28 | Pi=pi 29 | 30 | 31 | 32 | ## (*For calculating the Schmidt-Rank Vector*) 33 | def toHH(expr): ## change the state form to calculation the SRV 34 | expr1=expr.replace(coeff*FF2[l1]*FF3[l2]*FF4[l3]*FF5[l4],sp.conjugate(coeff)*GG2[l1]*GG3[l2]*GG4[l3]*GG5[l4]) 35 | rho0=sp.expand(expr*expr1) 36 | rho0=rho0.replace(coeff*FF2[l1]*FF3[l2]*FF4[l3]*FF5[l4]*GG2[l5]*GG3[l6]*GG4[l7]*GG5[l8],coeff*HH[l1,l2,l3,l4,l5,l6,l7,l8]) 37 | return rho0 38 | 39 | def PartialTraceOne(expr,n): ##calculate the partial trace such as A|BCD 40 | 41 | dictadd=collect(expr, [HH[l1,l2,l3,l4,l5,l6,l7,l8]], evaluate=False) 42 | TermsCoeff=list(dictadd.items()) 43 | 44 | ParticleOne=[] 45 | ParticleTwo=[] 46 | ParticleThree=[] 47 | ## get the size of the matrix 48 | for ii in range(len(TermsCoeff)): 49 | HHList=TermsCoeff[ii][0] 50 | if HHList.indices[n-1]==HHList.indices[n+3]: 51 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5],HHList.indices[6],HHList.indices[7]] 52 | del(ll[n-1],ll[n+2]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 53 | 54 | ParticleOne.append(ll[0]) 55 | ParticleTwo.append(ll[1]) 56 | ParticleThree.append(ll[2]) 57 | # start from 0 58 | Upperone=max(ParticleOne)+1 59 | Lowerone=min(min(ParticleOne),0) 60 | Uppertwo=max(ParticleTwo)+1 61 | Lowertwo=min(min(ParticleTwo),0) 62 | Upperthree=max(ParticleThree)+1 63 | Lowerthree=min(min(ParticleThree),0) 64 | 65 | rangeP1=Upperone-Lowerone 66 | rangeP2=Uppertwo-Lowertwo 67 | rangeP3=Upperthree-Lowerthree 68 | 69 | Msize=(rangeP1*rangeP2*rangeP3) 70 | SMatrix=SparseMatrix(Msize, Msize, {(0, 0): 0}) 71 | 72 | for ii in range(len(TermsCoeff)): 73 | HHList=TermsCoeff[ii][0] 74 | if HHList.indices[n-1]==HHList.indices[n+3]: 75 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5],HHList.indices[6],HHList.indices[7]] 76 | del(ll[n-1],ll[n+2]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 77 | Dimrow=(ll[0]-Lowerone)*rangeP3*rangeP2+(ll[1]-Lowertwo)*rangeP3+(ll[2]-Lowerthree) 78 | Dimcol=(ll[3]-Lowerone)*rangeP3*rangeP2+(ll[4]-Lowertwo)*rangeP3+(ll[5]-Lowerthree) 79 | SMatrix=SparseMatrix(Msize, Msize, {(Dimrow,Dimcol):TermsCoeff[ii][1]})+SMatrix 80 | return SMatrix.rank() 81 | 82 | 83 | def PartialTraceTwo(expr,m,n): ##calculate the partial trace such as AB|CD 84 | 85 | dictadd=collect(expr, [HH[l1,l2,l3,l4,l5,l6,l7,l8]], evaluate=False) 86 | TermsCoeff=list(dictadd.items()) 87 | 88 | ParticleOne=[] 89 | ParticleTwo=[] 90 | ## get the size of the matrix 91 | for ii in range(len(TermsCoeff)): 92 | HHList=TermsCoeff[ii][0] 93 | if HHList.indices[m-1]==HHList.indices[m+3] and HHList.indices[n-1]==HHList.indices[n+3]: 94 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5],HHList.indices[6],HHList.indices[7]] 95 | del(ll[m-1]) 96 | del(ll[m+2]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 97 | del(ll[n-2]) 98 | del(ll[n]) 99 | ParticleOne.append(ll[0]) 100 | ParticleTwo.append(ll[1]) 101 | # start from 0 102 | Upperone=max(ParticleOne)+1 103 | Lowerone=min(min(ParticleOne),0) 104 | Uppertwo=max(ParticleTwo)+1 105 | Lowertwo=min(min(ParticleTwo),0) 106 | rangeP1=Upperone-Lowerone 107 | rangeP2=Uppertwo-Lowertwo 108 | 109 | Msize=(rangeP1*rangeP2) 110 | SMatrix=SparseMatrix(Msize, Msize, {(0, 0): 0}) 111 | 112 | for ii in range(len(TermsCoeff)): 113 | HHList=TermsCoeff[ii][0] 114 | if HHList.indices[m-1]==HHList.indices[m+3] and HHList.indices[n-1]==HHList.indices[n+3] : 115 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5],HHList.indices[6],HHList.indices[7]] 116 | ## because cannot del all at the same time, thus do it one by one, the index is not n+2 117 | del(ll[m-1]) 118 | del(ll[m+2]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 119 | del(ll[n-2]) 120 | del(ll[n]) 121 | Dimrow=(ll[0]-Lowerone)*rangeP2+(ll[1]-Lowertwo) 122 | Dimcol=(ll[2]-Lowerone)*rangeP2+(ll[3]-Lowertwo) 123 | SMatrix=SparseMatrix(Msize, Msize, {(Dimrow,Dimcol):TermsCoeff[ii][1]})+SMatrix 124 | return SMatrix.rank() 125 | 126 | 127 | ## The function to calculation the SRV of the quantum state 128 | def SchmidtRankVector(expr): 129 | rho=toHH(expr) 130 | if rho==0 :return [0,0,0,0,0,0,0] 131 | else: return [PartialTraceOne(rho,1),PartialTraceOne(rho,2),PartialTraceOne(rho,3),PartialTraceOne(rho,4),PartialTraceTwo(rho,1,2),PartialTraceTwo(rho,1,3),PartialTraceTwo(rho,1,4)] 132 | 133 | 134 | ## test: give a state (only consider same coefficient ) and returen the SRV number 135 | States=FF2[0]*FF3[0]*FF4[0]*FF5[0]+FF2[1]*FF3[1]*FF4[1]*FF5[0]+FF2[0]*FF3[2]*FF4[3]*FF5[1]; 136 | SRV=SchmidtRankVector(States) 137 | print('SRV: ',SRV) 138 | 139 | -------------------------------------------------------------------------------- /CalcSRV.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 31 23:36:08 2019 4 | 5 | @author: XuemeiGu 6 | email: njuxmgu@gmail.com 7 | 8 | """ 9 | 10 | ##### import packages 11 | import numpy as np 12 | import sympy as sp 13 | import random 14 | from numpy.random import choice 15 | from sympy.matrices import SparseMatrix 16 | from sympy import collect, expand, Symbol,sqrt,pi,I 17 | from itertools import combinations, chain 18 | 19 | a, b, c, d, e, f, FF1, FF2, FF3, FF4, FFn, HH, GG1, GG2, GG3, GG4=map(sp.IndexedBase,['a','b','c','d','e', 'f', 'FF1','FF2','FF3','FF4','FFn','HH','GG1','GG2','GG3','GG4']) 20 | l,l1, l2, l3, l4, l5, l6, l7, l8, x1, x2, x3, x4, x5, x6, coeff, powern =map(sp.Wild,['l','l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'coeff', 'powern']) 21 | 22 | zero=Symbol('zero') ## using in 4-fold coincidence 23 | psi=Symbol('psi') ## represent a quantum state 24 | 25 | ## corresponding symbolic number 26 | sqr2=sqrt(2)/2 ## equals to 1/sqrt(2) 27 | imagI=I ## equals to Imaginary 28 | Pi=pi 29 | 30 | PossiblePath=[('a','b'),('a','c'),('a','d'),('a','e'),('a','f'),('b','c'),('b','d'),('b','e'),('b','f'),('c','d'),('c','e'),('c','f'),('d','e'),('d','f'),('e','f')] 31 | PossiblePathNum=['a','b','c','d','e','f'] 32 | 33 | ## SPDC process 34 | def DownConvOAM(lorder,p1,p2): ## create the initial state,DC is a parameter 35 | initial_state=0 36 | for ii in range(-lorder,lorder+1): 37 | initial_state=p1[ii]*p2[-ii]+initial_state 38 | return initial_state 39 | 40 | ## define functions for OAM modes 41 | def BS_fun(expr,p1,p2): 42 | if expr.base==p1:return expr.replace(p1[l],sqr2*(p2[l]+imagI*p1[-l]),map=False, simultaneous=True, exact=False) 43 | else: return expr.replace(p2[l],sqr2*(p1[l]+imagI*p2[-l]),map=False, simultaneous=True, exact=False) 44 | 45 | def BS(psi,p1,p2): 46 | psi0=sp.expand(psi.replace(lambda expr: expr.base in [p1,p2], lambda expr: BS_fun(expr,p1,p2))) 47 | return psi0 48 | 49 | def LI_fun(expr,p1,p2): 50 | if expr.base==p1:return expr.replace(p1[l1],(sp.cos(l1*Pi/2)**2)*p1[l1]+imagI*(sp.sin(l1*Pi/2)**2)*p2[-l1],map=False, simultaneous=True, exact=False) 51 | else: return expr.replace(p2[l1],-(sp.cos(l1*Pi/2)**2)*p2[l1]+imagI*(sp.sin(l1*Pi/2)**2)*p1[-l1],map=False, simultaneous=True, exact=False) 52 | 53 | def LI(psi,p1,p2): 54 | psi0=sp.expand(psi.replace(lambda expr: expr.base in [p1,p2], lambda expr: LI_fun(expr,p1,p2))) 55 | return psi0 56 | 57 | def Reflection(expr, p): 58 | expr=expr.replace(p[l1],imagI*p[-l1], map=False, simultaneous=True, exact=False) 59 | return expr 60 | 61 | def OAMHolo(expr, p, n): 62 | expr=expr.replace(p[l1],p[l1+n], map=False, simultaneous=True, exact=False) 63 | return expr 64 | 65 | def DP(expr, p): 66 | expr=expr.replace(p[l1],imagI*sp.exp(imagI*l1*(Pi))*p[-l1], map=False, simultaneous=True, exact=False) 67 | return expr 68 | 69 | ## make 4-fold coincidence 70 | def replaceRule(expr, repls): 71 | for k, m in repls.items(): 72 | expr = expr.replace(k, m, map=False, simultaneous=True, exact=False) 73 | return expr 74 | 75 | def MakeFF2(expr): 76 | NFoldrepls = {coeff*a[l1]*a[l2] : 0, coeff*b[l1]*b[l2] : 0, coeff*c[l1]*c[l2] : 0, coeff*d[l1]*d[l2] : 0} 77 | expr1=replaceRule(expr,NFoldrepls) 78 | NFoldrepls={coeff*a[l2]*b[l3]*c[l4]*d[l5]: 0} 79 | expr2=replaceRule(expr1,NFoldrepls) 80 | expr=expr1-expr2 81 | expr=expr.replace(coeff*a[l1]*b[l2]*c[l3]*d[l4],coeff*FF1[l1]*FF2[l2]*FF3[l3]*FF4[l4]) 82 | return expr 83 | 84 | ## trigger 85 | def Trigger(expr,p1,nlist): ##must be used after MakeFF2 in path p1 86 | lt=sp.Wild('lt',exclude=nlist) 87 | expr1=expr.replace(p1[lt],0) 88 | expr=expr1.replace(p1[l],1) 89 | return expr 90 | 91 | def Allsubsets(iterable): ## create whole strings from device number 92 | xs = list(iterable) 93 | return list(chain.from_iterable(combinations(xs,n) for n in range(len(xs)+1))) 94 | 95 | 96 | ## functions for maximum entangled SRV state 97 | 98 | # retutn [(terms, coeff),(terms, coeff),...] 99 | def TermsCoeffList(expr): # retutn [(terms, coeff),(terms, coeff),...] 100 | dictadd=collect(expr, [FF2[x1]*FF3[x2]*FF4[x3]], evaluate=False) 101 | TermsCoeff=list(dictadd.items()) 102 | return TermsCoeff 103 | 104 | ## return the number of the terms in the 4-fold state and check the equality for coeffs 105 | def CheckTermCoeffOfState(TermsCoeff): 106 | coeffvalue={} 107 | for ii in range(len(TermsCoeff)): 108 | coefftemp=TermsCoeff[ii][1] 109 | # print('coefftemp ',coefftemp) 110 | coeffvalue[ii]=expand(coefftemp*sp.conjugate(coefftemp)) 111 | Equlflag=0 112 | for iii in range(len(TermsCoeff)): 113 | if iii < len(TermsCoeff)-1: 114 | if coeffvalue[iii]==coeffvalue[iii+1]: 115 | Equlflag=1 116 | else: 117 | Equlflag=0 118 | break 119 | return [len(TermsCoeff), Equlflag] 120 | 121 | def AllTypesOfFFn(expr,FFn): ## find all the used mode in path (no repeated modes) 122 | dictadd=collect(expr, [FFn[x1]], evaluate=False) 123 | TermsCoeff=list(dictadd.items()) 124 | NumOfPath=[] 125 | for ii in range(len(TermsCoeff)): 126 | HHList=TermsCoeff[ii][0] 127 | NumOfPath.append(HHList.indices[0]) 128 | return set(NumOfPath) ## returen used modes in patha 129 | 130 | ## (*For calculating the Schmidt-Rank Vector*) 131 | def toHH(expr): ## change the state form to calculation the SRV 132 | expr1=expr.replace(coeff*FF2[l1]*FF3[l2]*FF4[l3],sp.conjugate(coeff)*GG2[l1]*GG3[l2]*GG4[l3]) 133 | rho0=sp.expand(expr*expr1) 134 | rho0=rho0.replace(coeff*FF2[l1]*FF3[l2]*FF4[l3]*GG2[l4]*GG3[l5]*GG4[l6],coeff*HH[l1,l2,l3,l4,l5,l6]) 135 | return rho0 136 | 137 | def PartialTrace(expr,n): ##calculate the partial trace of the n_th photon 138 | 139 | dictadd=collect(expr, [HH[l1,l2,l3,l4,l5,l6]], evaluate=False) 140 | TermsCoeff=list(dictadd.items()) 141 | 142 | ParticleOne=[] 143 | ParticleTwo=[] 144 | ## get the size of the matrix 145 | for ii in range(len(TermsCoeff)): 146 | HHList=TermsCoeff[ii][0] 147 | if HHList.indices[n-1]==HHList.indices[n+2] : 148 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5]] 149 | del(ll[n-1],ll[n+1]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 150 | ParticleOne.append(ll[0]) 151 | ParticleTwo.append(ll[1]) 152 | # start from 0 153 | Upperone=max(ParticleOne)+1 154 | Lowerone=min(min(ParticleOne),0) 155 | Uppertwo=max(ParticleTwo)+1 156 | Lowertwo=min(min(ParticleTwo),0) 157 | rangeP1=Upperone-Lowerone 158 | rangeP2=Uppertwo-Lowertwo 159 | 160 | Msize=(rangeP1*rangeP2) 161 | SMatrix=SparseMatrix(Msize, Msize, {(0, 0): 0}) 162 | 163 | for ii in range(len(TermsCoeff)): 164 | HHList=TermsCoeff[ii][0] 165 | if HHList.indices[n-1]==HHList.indices[n+2] : 166 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5]] 167 | del(ll[n-1],ll[n+1]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 168 | # print('rest: ',ll) 169 | # print('rest: ',ll[0]-Lowerone,'',ll[1]-Lowertwo, '',ll[2]-Lowerone,'',ll[3]-Lowertwo) 170 | Dimrow=(ll[0]-Lowerone)*rangeP2+(ll[1]-Lowertwo) 171 | Dimcol=(ll[2]-Lowerone)*rangeP2+(ll[3]-Lowertwo) 172 | SMatrix=SparseMatrix(Msize, Msize, {(Dimrow,Dimcol):TermsCoeff[ii][1]})+SMatrix 173 | return SMatrix.rank() 174 | 175 | ## The function to calculation the SRV of the quantum state 176 | def SchmidtRankVector(expr): 177 | rho=toHH(expr) 178 | if rho==0 :return [0,0,0] 179 | else: return sorted([PartialTrace(rho,1),PartialTrace(rho,2),PartialTrace(rho,3)],reverse=True) 180 | 181 | 182 | ## for creating random setup 183 | def DefineActions(): ## creat the possible actions 184 | actions=[] 185 | for ii in range(len(PossiblePath)): 186 | PosA = PossiblePath[ii][0] 187 | PosB = PossiblePath[ii][1] 188 | actions.append("BS(XXX,"+PosA+","+PosB+")") 189 | actions.append("LI(XXX,"+PosA+","+PosB+")") 190 | 191 | for ii in range(6): 192 | Pos=PossiblePathNum[ii] 193 | actions.append("Reflection(XXX,"+Pos+")") 194 | actions.append("DP(XXX,"+Pos+")") 195 | nHOM=5 196 | HOM_list=list(range(-nHOM,nHOM+1)) 197 | HOM_list.remove(0) ## [-5, -4, -3, -2, -1, 1, 2, 3, 4, 5] 198 | for ii in range(1,len(HOM_list)+1): ## index strart from 1 199 | actions.append("OAMHolo(XXX,"+Pos+","+str(ii)+")") 200 | return actions 201 | 202 | 203 | def SetupStrList(actionTable,actionlist):## returen setup list 204 | rvATL=[] 205 | for iii in range(len(actionlist)): 206 | rvATL.append(actionTable[actionlist[iii]]) 207 | YYY='XXX' 208 | for kk in range(len(rvATL)-1,-1,-1): # in the inverted sequence 209 | YYY=YYY.replace('XXX',rvATL[kk]) 210 | return YYY 211 | 212 | def PrintOptions(expr,printflag): ## print 213 | if printflag==1: 214 | print(expr) 215 | 216 | 217 | PrintFlag=1 ## chose to give print out and only give the correct SRV 218 | 219 | actions=DefineActions() 220 | random.seed() 221 | SizeOfAllList=1000000 222 | LoopNum=1000000000 223 | for iiLoop in range(LoopNum): ## create random setup 224 | iiNum=iiLoop%10 + 6 225 | AllListNum=choice(range(len(actions)), size=(SizeOfAllList,iiNum)) 226 | PrintOptions('**************************************************',PrintFlag) 227 | PrintOptions('-------create random lists of experiments-------',PrintFlag) 228 | PrintOptions(' ',PrintFlag) 229 | 230 | for iiLists in range(len(AllListNum)):# try every setuplist 231 | PrintOptions('-------start to try different experiments-------',PrintFlag) 232 | PrintOptions(' ',PrintFlag) 233 | 234 | setupStr=SetupStrList(actions, AllListNum[iiLists]) ## get the setup string 235 | 236 | OAMorder=1 # OAM number from -OAMorder to OAMorder 237 | crystal1=DownConvOAM(OAMorder,a,b) 238 | crystal2=DownConvOAM(OAMorder,c,d) 239 | DCState=(crystal1+crystal2)**2 ## produce photon pairs form SPDC 240 | 241 | FuncStr=setupStr.replace("XXX",str(DCState)) ## give the output state of the created setup 242 | output_state=expand(eval(FuncStr)) ## you can also use sp.sympify(FuncStr,mylist), don't know which is fast 243 | outputState=MakeFF2(output_state) 244 | 245 | AllTypesOfFF1=AllTypesOfFFn(outputState,FF1) 246 | AllCombfFF1=Allsubsets(AllTypesOfFF1) ## get all the subsets of FF1 for trigger 247 | 248 | 249 | for ii in range(1,len(AllCombfFF1)): ## try every trigger 250 | PrintOptions('-------start to try different triggers in Path A (FF1)-------',PrintFlag) 251 | PrintOptions(' ',PrintFlag) 252 | 253 | lowoutput_state=outputState 254 | ReducedVV=Trigger(lowoutput_state,FF1,list(AllCombfFF1[ii])) 255 | PrintOptions('Small_SPDC Output State for the setup: '+str(ReducedVV),PrintFlag) 256 | PrintOptions(' ',PrintFlag) 257 | 258 | AllTypesOfFF2=AllTypesOfFFn(ReducedVV,FF2) ## store the FF2 FF3 FF4 in a list 259 | AllTypesOfFF3=AllTypesOfFFn(ReducedVV,FF3) 260 | AllTypesOfFF4=AllTypesOfFFn(ReducedVV,FF4) 261 | 262 | PrintOptions('AllTypesOfFF2: FF2 '+str(AllTypesOfFF2)+';',PrintFlag) 263 | PrintOptions('AllTypesOfFF3: FF3 '+str(AllTypesOfFF3)+';',PrintFlag) 264 | PrintOptions('AllTypesOfFF4: FF4 '+str(AllTypesOfFF4)+';',PrintFlag) 265 | PrintOptions(' ',PrintFlag) 266 | DimVecEncode=sorted([len(AllTypesOfFF2),len(AllTypesOfFF3),len(AllTypesOfFF4)],reverse=True) ## get the maximum number of used dimensions 267 | 268 | ## check all the coefficients in the Output state are the same and the number of the term 269 | ReducedVVList = TermsCoeffList(ReducedVV) 270 | TermofState, CoeffsEqual=CheckTermCoeffOfState(ReducedVVList) 271 | PrintOptions('TermofState: '+str(TermofState)+' CoeffsEqual (0: No; 1: Yes)? '+str(CoeffsEqual),PrintFlag) 272 | PrintOptions(' ',PrintFlag) 273 | 274 | if DimVecEncode[0]1 and InfoLetter=='_OKTerms_MaxEnt' : #the index starts from 0 289 | DimVec=SchmidtRankVector(ReducedVV) 290 | PrintOptions('SRV: '+str(DimVec),PrintFlag) 291 | PrintOptions(' ',PrintFlag) 292 | PrintOptions('The corresponing setup: '+setupStr+'; FF1: '+str(list(AllCombfFF1[ii])),PrintFlag) 293 | PrintOptions(' ',PrintFlag) 294 | 295 | if DimVec[2]>1 and TermofState== DimVec[0]: ## check maximun entangled states 296 | 297 | PrintOptions('No problems in lower OAM orders, Now let us check higher order OAM SPDC...',PrintFlag) 298 | PrintOptions(' ',PrintFlag) 299 | OAMorder=5 # check high-order OAM modes 300 | crystal1=DownConvOAM(OAMorder,a,b) 301 | crystal2=DownConvOAM(OAMorder,c,d) 302 | DCState=(crystal1+crystal2)**2 303 | 304 | FuncStr=setupStr.replace("XXX",str(DCState)) ## give the output state of the created setup 305 | highoutput_state=expand(eval(FuncStr)) 306 | highoutputState=MakeFF2(highoutput_state) 307 | 308 | ReducedVVHO=Trigger(highoutputState,FF1,list(AllCombfFF1[ii])) 309 | 310 | AllTypesOfFF2Full=AllTypesOfFFn(ReducedVVHO,FF2) ## store the FF2 FF3 FF4 in a list 311 | AllTypesOfFF3Full=AllTypesOfFFn(ReducedVVHO,FF3) 312 | AllTypesOfFF4Full=AllTypesOfFFn(ReducedVVHO,FF4) 313 | PrintOptions('AllTypesOfFF2Full: FF2 '+str(AllTypesOfFF2Full)+';',PrintFlag) 314 | PrintOptions('AllTypesOfFF3Full: FF3 '+str(AllTypesOfFF3Full)+';',PrintFlag) 315 | PrintOptions('AllTypesOfFF4Full: FF4 '+str(AllTypesOfFF4Full)+';',PrintFlag) 316 | PrintOptions(' ',PrintFlag) 317 | 318 | ComplementDC2=list(AllTypesOfFF2Full.difference(AllTypesOfFF2)) 319 | ComplementDC3=list(AllTypesOfFF3Full.difference(AllTypesOfFF3)) 320 | ComplementDC4=list(AllTypesOfFF4Full.difference(AllTypesOfFF4)) 321 | if ComplementDC2 != []: 322 | for iii in range(len(ComplementDC2)): 323 | ReducedVVHO=ReducedVVHO.replace(FF2[ComplementDC2[iii]],0, map=False, simultaneous=True, exact=False) 324 | if ComplementDC3 != []: 325 | for iii in range(len(ComplementDC3)): 326 | ReducedVVHO=ReducedVVHO.replace(FF3[ComplementDC3[iii]],0, map=False, simultaneous=True, exact=False) 327 | if ComplementDC4 != []: 328 | for iii in range(len(ComplementDC4)): 329 | ReducedVVHO=ReducedVVHO.replace(FF4[ComplementDC4[iii]],0, map=False, simultaneous=True, exact=False) 330 | 331 | PrintOptions('Large_SPDC Output State for the setup: '+str(ReducedVVHO),PrintFlag) 332 | PrintOptions(' ',PrintFlag) 333 | if ReducedVV==ReducedVVHO: 334 | print('This is a good SRV:', DimVec) 335 | inforStr= 'SRV:'+str(DimVec)+'; Setup: '+setupStr+'; FF1:'+str(list(AllCombfFF1[ii]))+'\r\n' 336 | print('info: ',inforStr) 337 | goodlist = open("Goodcases.txt", "a+") 338 | goodlist.write(inforStr) 339 | goodlist.close() 340 | else : 341 | print('This is a bad SRV beacuse of high-order OAM', DimVec) 342 | inforStr= 'SRV:'+str(DimVec)+'; Setup: '+setupStr+'; FF1:'+str(list(AllCombfFF1[ii]))+'\r\n' 343 | print('info: ',inforStr) 344 | badlist = open("Badcases.txt", "a+") 345 | badlist.write(inforStr) 346 | badlist.close() 347 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2023 Xuemei Gu 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Melvin 2 | 3 | 4 | 5 | [Automated Search for new Quantum Experiments](https://doi.org/10.1103/PhysRevLett.116.090405)\ 6 |         Phys. Rev. Lett. 116(9), 090405 (2016)\ 7 |         *Mario Krenn, Mehul Malik, Robert Fickler, Radek Lapkiewicz, and Anton Zeilinger* 8 | 9 | 10 | Melvin is an innovative computer program designed to assist quantum physicists in the development and discovery of novel and beneficial quantum experiments. It was developed by [Mario Krenn](https://mariokrenn.wordpress.com/), marking a significant advancement in the field of quantum physics. 11 | 12 | Here, I have partially reimplemented Melvin, originally developed in Mathematica, in Python utilizing SymPy for symbolic mathematics. 13 | 14 | ## Prerequisites: 15 | 16 | To run the code from this repository, you need to install SymPy - **version 1.3**. 17 | 18 | ## Codes 19 | 20 | Examples on creating quantum states and symbolic transformations: \ 21 | Related Mathematica codes are in the branch [Mathematica_Codes](https://github.com/XuemeiGu/MelvinPython/tree/Mathematica_Codes): 22 | 23 | * The SimpleHOMExample program shows how to work with quantum states, and how the symbolic transformations work. 24 | ``` 25 | SimpleHOMExample.nb 26 | SimpleHOMExample.py 27 | ``` 28 | * The CalcSRV program is a full version which searches for 3-particle high-dimensionally entangled states with existing optical elements. 29 | ``` 30 | CalcSRV.nb 31 | CalcSRV.py 32 | ``` 33 | * The SRVCaseCheck code is for checking whether a optical setup can produce 3-particle maximally entangled states. 34 | ``` 35 | SRVCaseCheck.py 36 | ``` 37 | 38 | * Here I add one more code for checking the SchmidtRankVector of a 4-particle maximally entangled state\ 39 | More details about Schmidt Rank Vectors, refer to [Structure of Multidimensional Entanglement in Multipartite Systems](https://doi.org/10.1103/PhysRevLett.110.030501). 40 | ``` 41 | Calc4ParticleSRV.nb 42 | Calc4ParticleSRV.py 43 | ``` 44 | 45 | ## How to cite 46 | if you want to cite the code for your work, you can use the following: 47 | ``` 48 | @misc{melvin_code, 49 | author = {Xuemei Gu}, 50 | title = {Melvin: Automated Search for new Quantum Experiments -- Python Version}, 51 | year = {2019}, 52 | howpublished = {\url{https://github.com/XuemeiGu/MelvinPython}}, 53 | note = {Accessed: 2019-04-03} 54 | } 55 | ``` 56 | 57 | If you have any problems, please do not hesitate to contact me\ 58 | Email: njuxmgu@gmail.com or xuemei.gu@mpl.mpg.de 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | -------------------------------------------------------------------------------- /SRVCaseCheck.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sun Mar 31 23:36:08 2019 4 | 5 | @author: XuemeiGu 6 | email: njuxmgu@gmail.com 7 | 8 | """ 9 | 10 | # -*- coding: utf-8 -*- 11 | """ 12 | Created on Sun Mar 31 23:36:08 2019 13 | 14 | @author: XuemeiGu 15 | email: njuxmgu@gmail.com 16 | 17 | """ 18 | 19 | ##### import packages 20 | import numpy as np 21 | import sympy as sp 22 | import random 23 | from numpy.random import choice 24 | from sympy.matrices import SparseMatrix 25 | from sympy import collect, expand, Symbol,sqrt,pi,I 26 | from itertools import combinations, chain 27 | 28 | a, b, c, d, e, f, FF1, FF2, FF3, FF4, FFn, HH, GG1, GG2, GG3, GG4=map(sp.IndexedBase,['a','b','c','d','e', 'f', 'FF1','FF2','FF3','FF4','FFn','HH','GG1','GG2','GG3','GG4']) 29 | l,l1, l2, l3, l4, l5, l6, l7, l8, x1, x2, x3, x4, x5, x6, coeff, powern =map(sp.Wild,['l','l1', 'l2', 'l3', 'l4', 'l5', 'l6', 'l7', 'l8', 'x1', 'x2', 'x3', 'x4', 'x5', 'x6', 'coeff', 'powern']) 30 | 31 | zero=Symbol('zero') ## using in 4-fold coincidence 32 | psi=Symbol('psi') ## represent a quantum state 33 | 34 | ## corresponding symbolic number 35 | sqr2=sqrt(2)/2 ## equals to 1/sqrt(2) 36 | imagI=I ## equals to Imaginary 37 | Pi=pi 38 | 39 | PossiblePath=[('a','b'),('a','c'),('a','d'),('a','e'),('a','f'),('b','c'),('b','d'),('b','e'),('b','f'),('c','d'),('c','e'),('c','f'),('d','e'),('d','f'),('e','f')] 40 | PossiblePathNum=['a','b','c','d','e','f'] 41 | 42 | ## SPDC process 43 | def DownConvOAM(lorder,p1,p2): ## create the initial state,DC is a parameter 44 | initial_state=0 45 | for ii in range(-lorder,lorder+1): 46 | initial_state=p1[ii]*p2[-ii]+initial_state 47 | return initial_state 48 | 49 | ## define functions for OAM modes 50 | def BS_fun(expr,p1,p2): 51 | if expr.base==p1:return expr.replace(p1[l],sqr2*(p2[l]+imagI*p1[-l]),map=False, simultaneous=True, exact=False) 52 | else: return expr.replace(p2[l],sqr2*(p1[l]+imagI*p2[-l]),map=False, simultaneous=True, exact=False) 53 | 54 | def BS(psi,p1,p2): 55 | psi0=sp.expand(psi.replace(lambda expr: expr.base in [p1,p2], lambda expr: BS_fun(expr,p1,p2))) 56 | return psi0 57 | 58 | def LI_fun(expr,p1,p2): 59 | if expr.base==p1:return expr.replace(p1[l1],(sp.cos(l1*Pi/2)**2)*p1[l1]+imagI*(sp.sin(l1*Pi/2)**2)*p2[-l1],map=False, simultaneous=True, exact=False) 60 | else: return expr.replace(p2[l1],-(sp.cos(l1*Pi/2)**2)*p2[l1]+imagI*(sp.sin(l1*Pi/2)**2)*p1[-l1],map=False, simultaneous=True, exact=False) 61 | 62 | def LI(psi,p1,p2): 63 | psi0=sp.expand(psi.replace(lambda expr: expr.base in [p1,p2], lambda expr: LI_fun(expr,p1,p2))) 64 | return psi0 65 | 66 | def Reflection(expr, p): 67 | expr=expr.replace(p[l1],imagI*p[-l1], map=False, simultaneous=True, exact=False) 68 | return expr 69 | 70 | def OAMHolo(expr, p, n): 71 | expr=expr.replace(p[l1],p[l1+n], map=False, simultaneous=True, exact=False) 72 | return expr 73 | 74 | def DP(expr, p): 75 | expr=expr.replace(p[l1],imagI*sp.exp(imagI*l1*(Pi))*p[-l1], map=False, simultaneous=True, exact=False) 76 | return expr 77 | 78 | ## make 4-fold coincidence 79 | def replaceRule(expr, repls): 80 | for k, m in repls.items(): 81 | expr = expr.replace(k, m, map=False, simultaneous=True, exact=False) 82 | return expr 83 | 84 | def MakeFF2(expr): 85 | NFoldrepls = {coeff*a[l1]*a[l2] : 0, coeff*b[l1]*b[l2] : 0, coeff*c[l1]*c[l2] : 0, coeff*d[l1]*d[l2] : 0} 86 | expr1=replaceRule(expr,NFoldrepls) 87 | NFoldrepls={coeff*a[l2]*b[l3]*c[l4]*d[l5]: 0} 88 | expr2=replaceRule(expr1,NFoldrepls) 89 | expr=expr1-expr2 90 | expr=expr.replace(coeff*a[l1]*b[l2]*c[l3]*d[l4],coeff*FF1[l1]*FF2[l2]*FF3[l3]*FF4[l4]) 91 | return expr 92 | 93 | ## trigger 94 | def Trigger(expr,p1,nlist): ##must be used after MakeFF2 in path p1 95 | lt=sp.Wild('lt',exclude=nlist) 96 | expr1=expr.replace(p1[lt],0) 97 | expr=expr1.replace(p1[l],1) 98 | return expr 99 | 100 | def Allsubsets(iterable): ## create whole strings from device number 101 | xs = list(iterable) 102 | return list(chain.from_iterable(combinations(xs,n) for n in range(len(xs)+1))) 103 | 104 | 105 | ## functions for maximum entangled SRV state 106 | 107 | # retutn [(terms, coeff),(terms, coeff),...] 108 | def TermsCoeffList(expr): # retutn [(terms, coeff),(terms, coeff),...] 109 | dictadd=collect(expr, [FF2[x1]*FF3[x2]*FF4[x3]], evaluate=False) 110 | TermsCoeff=list(dictadd.items()) 111 | return TermsCoeff 112 | 113 | ## return the number of the terms in the 4-fold state and check the equality for coeffs 114 | def CheckTermCoeffOfState(TermsCoeff): 115 | coeffvalue={} 116 | for ii in range(len(TermsCoeff)): 117 | coefftemp=TermsCoeff[ii][1] 118 | # print('coefftemp ',coefftemp) 119 | coeffvalue[ii]=expand(coefftemp*sp.conjugate(coefftemp)) 120 | Equlflag=0 121 | for iii in range(len(TermsCoeff)): 122 | if iii < len(TermsCoeff)-1: 123 | if coeffvalue[iii]==coeffvalue[iii+1]: 124 | Equlflag=1 125 | else: 126 | Equlflag=0 127 | break 128 | return [len(TermsCoeff), Equlflag] 129 | 130 | def AllTypesOfFFn(expr,FFn): ## find all the used mode in path (no repeated modes) 131 | dictadd=collect(expr, [FFn[x1]], evaluate=False) 132 | TermsCoeff=list(dictadd.items()) 133 | NumOfPath=[] 134 | for ii in range(len(TermsCoeff)): 135 | HHList=TermsCoeff[ii][0] 136 | NumOfPath.append(HHList.indices[0]) 137 | return set(NumOfPath) ## returen used modes in patha 138 | 139 | ## (*For calculating the Schmidt-Rank Vector*) 140 | def toHH(expr): ## change the state form to calculation the SRV 141 | expr1=expr.replace(coeff*FF2[l1]*FF3[l2]*FF4[l3],sp.conjugate(coeff)*GG2[l1]*GG3[l2]*GG4[l3]) 142 | rho0=sp.expand(expr*expr1) 143 | rho0=rho0.replace(coeff*FF2[l1]*FF3[l2]*FF4[l3]*GG2[l4]*GG3[l5]*GG4[l6],coeff*HH[l1,l2,l3,l4,l5,l6]) 144 | return rho0 145 | 146 | def PartialTrace(expr,n): ##calculate the partial trace of the n_th photon 147 | 148 | dictadd=collect(expr, [HH[l1,l2,l3,l4,l5,l6]], evaluate=False) 149 | TermsCoeff=list(dictadd.items()) 150 | 151 | ParticleOne=[] 152 | ParticleTwo=[] 153 | ## get the size of the matrix 154 | for ii in range(len(TermsCoeff)): 155 | HHList=TermsCoeff[ii][0] 156 | if HHList.indices[n-1]==HHList.indices[n+2] : 157 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5]] 158 | del(ll[n-1],ll[n+1]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 159 | ParticleOne.append(ll[0]) 160 | ParticleTwo.append(ll[1]) 161 | # start from 0 162 | Upperone=max(ParticleOne)+1 163 | Lowerone=min(min(ParticleOne),0) 164 | Uppertwo=max(ParticleTwo)+1 165 | Lowertwo=min(min(ParticleTwo),0) 166 | rangeP1=Upperone-Lowerone 167 | rangeP2=Uppertwo-Lowertwo 168 | 169 | Msize=(rangeP1*rangeP2) 170 | SMatrix=SparseMatrix(Msize, Msize, {(0, 0): 0}) 171 | 172 | for ii in range(len(TermsCoeff)): 173 | HHList=TermsCoeff[ii][0] 174 | if HHList.indices[n-1]==HHList.indices[n+2] : 175 | ll=[HHList.indices[0],HHList.indices[1],HHList.indices[2],HHList.indices[3],HHList.indices[4],HHList.indices[5]] 176 | del(ll[n-1],ll[n+1]) ## because cannot del all at the same time, thus do it one by one, the index is not n+2 177 | # print('rest: ',ll) 178 | # print('rest: ',ll[0]-Lowerone,'',ll[1]-Lowertwo, '',ll[2]-Lowerone,'',ll[3]-Lowertwo) 179 | Dimrow=(ll[0]-Lowerone)*rangeP2+(ll[1]-Lowertwo) 180 | Dimcol=(ll[2]-Lowerone)*rangeP2+(ll[3]-Lowertwo) 181 | SMatrix=SparseMatrix(Msize, Msize, {(Dimrow,Dimcol):TermsCoeff[ii][1]})+SMatrix 182 | return SMatrix.rank() 183 | 184 | ## The function to calculation the SRV of the quantum state 185 | def SchmidtRankVector(expr): 186 | rho=toHH(expr) 187 | if rho==0 :return [0,0,0] 188 | else: return sorted([PartialTrace(rho,1),PartialTrace(rho,2),PartialTrace(rho,3)],reverse=True) 189 | 190 | 191 | ## for creating random setup 192 | def DefineActions(): ## creat the possible actions 193 | actions=[] 194 | for ii in range(len(PossiblePath)): 195 | PosA = PossiblePath[ii][0] 196 | PosB = PossiblePath[ii][1] 197 | actions.append("BS(XXX,"+PosA+","+PosB+")") 198 | actions.append("LI(XXX,"+PosA+","+PosB+")") 199 | 200 | for ii in range(6): 201 | Pos=PossiblePathNum[ii] 202 | actions.append("Reflection(XXX,"+Pos+")") 203 | actions.append("DP(XXX,"+Pos+")") 204 | nHOM=5 205 | HOM_list=list(range(-nHOM,nHOM+1)) 206 | HOM_list.remove(0) ## [-5, -4, -3, -2, -1, 1, 2, 3, 4, 5] 207 | for ii in range(1,len(HOM_list)+1): ## index strart from 1 208 | actions.append("OAMHolo(XXX,"+Pos+","+str(ii)+")") 209 | return actions 210 | 211 | 212 | def SetupStrList(actionTable,actionlist):## returen setup list 213 | rvATL=[] 214 | for iii in range(len(actionlist)): 215 | rvATL.append(actionTable[actionlist[iii]]) 216 | YYY='XXX' 217 | for kk in range(len(rvATL)-1,-1,-1): # in the inverted sequence 218 | YYY=YYY.replace('XXX',rvATL[kk]) 219 | return YYY 220 | 221 | def PrintOptions(expr,printflag):## returen setup list 222 | if printflag==1: 223 | print(expr) 224 | 225 | 226 | 227 | 228 | 229 | ''' 230 | #good example: Reflection(OAMHolo(OAMHolo(OAMHolo(OAMHolo(LI(DCState,b,c),a,-2),a,-2),c,4),b,4),a) trigger: FF1,[3,4] 231 | #bad example: OAMHolo(OAMHolo(BS(BS(OAMHolo(OAMHolo(OAMHolo(OAMHolo(DCState,c,-3),a,3),f,-5),c,2),a,c),a,b),b,-3),e,-4) trigger: FF1,[1] 232 | ''' 233 | ''' 234 | good examples: 235 | SRV:[3, 3, 2]; Setup: OAMHolo(OAMHolo(OAMHolo(LI(OAMHolo(Reflection(XXX,e),a,3),b,c),d,5),a,2),e,7); FF1:[4, 5] 236 | SRV:[3, 3, 2]; Setup: OAMHolo(OAMHolo(OAMHolo(LI(OAMHolo(Reflection(XXX,e),a,3),b,c),d,5),a,2),e,7); FF1:[5, 6] 237 | SRV:[3, 3, 2]; Setup: OAMHolo(DP(OAMHolo(LI(OAMHolo(OAMHolo(XXX,b,2),f,8),a,d),f,3),b),c,2); FF1:[0, 1] 238 | SRV:[3, 3, 2]; Setup: OAMHolo(DP(OAMHolo(LI(OAMHolo(OAMHolo(XXX,b,2),f,8),a,d),f,3),b),c,2); FF1:[0, -1] 239 | 240 | ''' 241 | 242 | PrintFlag=1 ## chose to give print out and only give the correct SRV 243 | 244 | setupStr='OAMHolo(OAMHolo(OAMHolo(LI(OAMHolo(Reflection(XXX,e),a,3),b,c),d,5),a,2),e,7)' 245 | triggers=[4,5] 246 | 247 | #setupStr='OAMHolo(OAMHolo(BS(BS(OAMHolo(OAMHolo(OAMHolo(OAMHolo(DCState,c,-3),a,3),f,-5),c,2),a,c),a,b),b,-3),e,-4)' 248 | #triggers=[1] 249 | 250 | OAMorder=1 # OAM number from -OAMorder to OAMorder 251 | crystal1=DownConvOAM(OAMorder,a,b) 252 | crystal2=DownConvOAM(OAMorder,c,d) 253 | DCState=(crystal1+crystal2)**2 ## produce photon pairs form SPDC 254 | 255 | FuncStr=setupStr.replace("XXX",str(DCState)) ## give the output state of the created setup 256 | output_state=expand(eval(FuncStr)) ## you can also use sp.sympify(FuncStr,mylist), don't know which is fast 257 | outputState=MakeFF2(output_state) 258 | PrintOptions(' ',PrintFlag) 259 | PrintOptions('-------give the output of one experimental setup-------',PrintFlag) 260 | PrintOptions(' ',PrintFlag) 261 | 262 | output_state=outputState 263 | ReducedVV=Trigger(output_state,FF1,triggers) 264 | PrintOptions('Small_SPDC Output State for the setup: '+str(ReducedVV),PrintFlag) 265 | PrintOptions(' ',PrintFlag) 266 | 267 | AllTypesOfFF2=AllTypesOfFFn(ReducedVV,FF2) ## store the FF2 FF3 FF4 in a list 268 | AllTypesOfFF3=AllTypesOfFFn(ReducedVV,FF3) 269 | AllTypesOfFF4=AllTypesOfFFn(ReducedVV,FF4) 270 | 271 | PrintOptions('AllTypesOfFF2: FF2 '+str(AllTypesOfFF2)+';',PrintFlag) 272 | PrintOptions('AllTypesOfFF3: FF3 '+str(AllTypesOfFF3)+';',PrintFlag) 273 | PrintOptions('AllTypesOfFF4: FF4 '+str(AllTypesOfFF4)+';',PrintFlag) 274 | PrintOptions(' ',PrintFlag) 275 | DimVecEncode=sorted([len(AllTypesOfFF2),len(AllTypesOfFF3),len(AllTypesOfFF4)],reverse=True) ## get the maximum number of used dimensions 276 | 277 | ## check all the coefficients in the Output state are the same and the number of the term 278 | ReducedVVList = TermsCoeffList(ReducedVV) 279 | TermofState, CoeffsEqual=CheckTermCoeffOfState(ReducedVVList) 280 | PrintOptions('TermofState: '+str(TermofState)+' CoeffsEqual? '+str(CoeffsEqual),PrintFlag) 281 | PrintOptions(' ',PrintFlag) 282 | 283 | if DimVecEncode[0]1 and InfoLetter=='_OKTerms_MaxEnt' : #the index starts from 0 297 | DimVec=SchmidtRankVector(ReducedVV) 298 | PrintOptions('SRV: '+str(DimVec),PrintFlag) 299 | PrintOptions(' ',PrintFlag) 300 | 301 | if DimVec[2]>1 and TermofState== DimVec[0]: ## check maximun entangled states 302 | 303 | PrintOptions('No problems in lower OAM orders, Now let us check higher order OAM SPDC...',PrintFlag) 304 | PrintOptions(' ',PrintFlag) 305 | OAMorder=5 # check high-order OAM modes 306 | crystal1=DownConvOAM(OAMorder,a,b) 307 | crystal2=DownConvOAM(OAMorder,c,d) 308 | DCState=(crystal1+crystal2)**2 309 | 310 | FuncStr=setupStr.replace("XXX",str(DCState)) ## give the output state of the created setup 311 | highoutput_state=expand(eval(FuncStr)) 312 | highoutputState=MakeFF2(highoutput_state) 313 | 314 | ReducedVVHO=Trigger(highoutputState,FF1,triggers) 315 | 316 | AllTypesOfFF2Full=AllTypesOfFFn(ReducedVVHO,FF2) ## store the FF2 FF3 FF4 in a list 317 | AllTypesOfFF3Full=AllTypesOfFFn(ReducedVVHO,FF3) 318 | AllTypesOfFF4Full=AllTypesOfFFn(ReducedVVHO,FF4) 319 | PrintOptions('AllTypesOfFF2Full: FF2 '+str(AllTypesOfFF2Full)+';',PrintFlag) 320 | PrintOptions('AllTypesOfFF3Full: FF3 '+str(AllTypesOfFF3Full)+';',PrintFlag) 321 | PrintOptions('AllTypesOfFF4Full: FF4 '+str(AllTypesOfFF4Full)+';',PrintFlag) 322 | PrintOptions(' ',PrintFlag) 323 | 324 | ComplementDC2=list(AllTypesOfFF2Full.difference(AllTypesOfFF2)) 325 | ComplementDC3=list(AllTypesOfFF3Full.difference(AllTypesOfFF3)) 326 | ComplementDC4=list(AllTypesOfFF4Full.difference(AllTypesOfFF4)) 327 | if ComplementDC2 != []: 328 | for ii in range(len(ComplementDC2)): 329 | ReducedVVHO=ReducedVVHO.replace(FF2[ComplementDC2[ii]],0, map=False, simultaneous=True, exact=False) 330 | if ComplementDC3 != []: 331 | for ii in range(len(ComplementDC3)): 332 | ReducedVVHO=ReducedVVHO.replace(FF3[ComplementDC3[ii]],0, map=False, simultaneous=True, exact=False) 333 | if ComplementDC4 != []: 334 | for ii in range(len(ComplementDC4)): 335 | ReducedVVHO=ReducedVVHO.replace(FF4[ComplementDC4[ii]],0, map=False, simultaneous=True, exact=False) 336 | 337 | PrintOptions('Large_SPDC Output State for the setup: '+str(ReducedVVHO),PrintFlag) 338 | PrintOptions(' ',PrintFlag) 339 | if ReducedVV==ReducedVVHO: 340 | # PrintOptions('This is a good SRV:'+str(DimVec),PrintFlag) 341 | print('This is a good SRV:',str(DimVec)) 342 | PrintOptions(' ',PrintFlag) 343 | else : 344 | # PrintOptions('This is a bad SRV beacuse of high-order OAM',PrintFlag) 345 | print('This is a bad SRV beacuse of high-order OAM') 346 | PrintOptions(' ',PrintFlag) 347 | 348 | 349 | 350 | 351 | -------------------------------------------------------------------------------- /SimpleHOMExample.py: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | """ 3 | Created on Sat Dec 8 16:23:21 2018 4 | 5 | @author: MG 6 | """ 7 | 8 | 9 | import sympy as sp 10 | 11 | # path information 12 | #a = sp.IndexedBase("a") 13 | #b = sp.IndexedBase("b") 14 | #c = sp.IndexedBase("c") 15 | #d = sp.IndexedBase("d") 16 | 17 | a, b, c, d, e, f= sp.symbols('a b c d e f', cls=sp.IndexedBase) 18 | FF1, FF2, FF3, FF4, FF5, FF6= sp.symbols('FF1 FF2 FF3 FF4 FF5 FF6', cls=sp.IndexedBase) 19 | x1, x2, x3, x4, coff= sp.symbols('x1 x2 x3 x4 coff', cls=sp.Wild) 20 | 21 | #polarization 22 | H,V=sp.symbols('H V') 23 | Modelist=[H,V] 24 | 25 | 26 | """ 27 | Polarization 28 | simpleHOMExample 29 | 30 | -> Hong-Ou-Mandel destructive interference 31 | -> Hong-Ou-Mandel constructive interference 32 | -> 4-photon GHZ generation in postselection 33 | 34 | """ 35 | def DownConvPol(p1,p2): 36 | expr=p1[H]*p2[H] + p1[V]*p2[V] 37 | return expr 38 | 39 | def HWP(expr,p): 40 | #expr=expr.subs([(p[H],p[V]),(p[V],p[H])],simultaneous=True) 41 | expr=expr.subs([(p[H],p[V]),(p[V],-p[H])],simultaneous=True) # in your code, V change to -H 42 | return expr 43 | 44 | def BSPol(expr,p1,p2): 45 | expr=expr.subs([(p1[x1],(p2[x1]+sp.I*p1[x1])/sp.sqrt(2)) for x1 in Modelist]+[(p2[x1],(p1[x1]+sp.I*p2[x1])/sp.sqrt(2)) for x1 in Modelist],simultaneous=True) 46 | return expr 47 | # 48 | def PBS(expr,p1,p2): 49 | expr=expr.subs([(p1[H],p2[H]),(p1[V],sp.I*p1[V]),(p2[H],p1[H]),(p2[V],sp.I*p2[V])],simultaneous=True) 50 | return expr 51 | 52 | # replaceRule --- _ Blank() and replace rules in mathematica; 53 | # cannot be used to BS and othert because of the simultaneous=True, need to be replace in once. 54 | def replaceRule(expr, repls): 55 | for k, m in repls.items(): 56 | expr = expr.replace(k, m, map=False, simultaneous=True, exact=False) 57 | return expr 58 | 59 | def MakeFF(expr): 60 | NFoldrepls = {coff*a[x1]*b[x2]*c[x3]*d[x4] : coff*FF1[x1]*FF2[x2]*FF3[x3]*FF4[x4], coff*a[x1]*a[x2] : 0, coff*b[x1]*b[x2] : 0, coff*c[x1]*c[x2] : 0, coff*d[x1]*d[x2] : 0} 61 | expr=replaceRule(expr,NFoldrepls) 62 | return expr 63 | 64 | 65 | """ 66 | We use a SPDC crystal which creates an entangled |phi+> state. 67 | Afterwards, we put a beam splitter. We will see Hong-Ou-Mandel interference 68 | (i.e. all photons in one the same output arm): 69 | """ 70 | print('Hong-Ou-Mandel destructive interference') 71 | print(' ') 72 | psi=DownConvPol(b,c) 73 | print('initial state: psi=',psi) 74 | 75 | 76 | psi2=sp.expand(BSPol(psi,b,c)) 77 | print('after BS: psi2=',psi2) 78 | 79 | 80 | """ 81 | Now we again create a SPDC, and change put a polariser at 90\[Degree] in arm b, 82 | and add a phase of i in arm b, leading to a |psi-> state: 83 | """ 84 | print(' ') 85 | print('Hong-Ou-Mandel constructive interference') 86 | print(' ') 87 | print('**********function calls step by step************') 88 | psi=DownConvPol(a,b) 89 | print('initial state: psi=',psi) 90 | 91 | psi2=HWP(psi,a) 92 | print('after the HWP in path a: psi2=',psi2) 93 | 94 | psi3=sp.expand(BSPol(psi2,a,b)) 95 | print('psi2 after the BS: psi3=',psi3) 96 | 97 | 98 | #the same can be achieved in a sequence of function calls 99 | psi3 = sp.expand(BSPol(HWP(DownConvPol(a,b),a),a,b)) 100 | print(' ') 101 | print('********a sequence of function calls**********') 102 | print('with HWP in path a and then BS, psi3=',psi3) 103 | 104 | 105 | 106 | """ 107 | The final example shows how the 4-photon Polarisation GHZ generation 108 | (using a PBS) can be described: 109 | """ 110 | print(' ') 111 | print('4-photon GHZ generation in postselection') 112 | print(' ') 113 | 114 | psi=DownConvPol(a,b)*DownConvPol(c,d) 115 | print('initial state: psi=',psi) 116 | 117 | psi2=sp.expand(PBS(psi,b,c)) 118 | print('after BS for path b and c: psi2=',psi2) 119 | 120 | psi3=MakeFF(psi2) 121 | print('4-fold coincidence: psi3=',psi3) 122 | 123 | 124 | 125 | 126 | --------------------------------------------------------------------------------