def decomp_4rdm_to_3rdm_sf(d4, d1, d2, d2_hom, d2_het, d3): """ Returns the list of terms composing the decomposition of the 4 body spin free rdm d4 in terms of one, two, and three body rdms" """ # This decomposition assumes the density matrices correspond to a spin averaged ensemble state. # d4 -- Spin free 4rdm to be decomposed # d1 -- One body spin free rdm # d2 -- Two body spin free rdm # d2_hom -- Sum of all alpha and all beta two body rdms. The "homogeneous spin" 2rdm. # d2_het -- Sum of abab and baba two body rdms. The "heterogeneous spin" 2rdm. # d3 -- Three body spin free rdm # Note that d2 = d2_hom + d2_het # Check input if ( not isinstance(d4, tensor) ) or (len(d4.indices) != 8): raise TypeError, "d4 must be a tensor with 8 indices" if ( not isinstance(d1, tensor) ) or (len(d1.indices) != 2): raise TypeError, "d1 must be a tensor with 2 indices" if ( not isinstance(d2, tensor) ) or (len(d2.indices) != 4): raise TypeError, "d2 must be a tensor with 4 indices" if ( not isinstance(d2_hom, tensor) ) or (len(d2_hom.indices) != 4): raise TypeError, "d2_hom must be a tensor with 4 indices" if ( not isinstance(d2_het, tensor) ) or (len(d2_het.indices) != 4): raise TypeError, "d2_het must be a tensor with 4 indices" if ( not isinstance(d3, tensor) ) or (len(d3.indices) != 6): raise TypeError, "d3 must be a tensor with 6 indices" # Initialize the return value decomp = [] # Compute terms from the sum involving the 3-body cumulant for p in range(4): ti = range(4) del(ti[p]) ti.insert(0,p) for q in range(4): bi = range(4) del(bi[q]) bi.insert(0,q) for i in range(1,4): for j in range(1,4): if i != j and ti[i] == bi[j]: temp = bi[i] bi[i] = bi[j] bi[j] = temp # Determine the number of index permutations n_perm = get_num_perms(ti,bi) # Determine the sign sign = (-1)**n_perm # Create the 1RDM 3RDM term bj = [i+4 for i in bi] coeff = sign * (0.5)**n_perm d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])] d3_copy_0 = d3.copy() d3_copy_0.indices = [d4.indices[i] for i in (ti[1:4] + bj[1:4])] decomp.append(term(coeff, [], [d1_copy_0, d3_copy_0])) # Create terms not involving the 3RDM d3_decomp = decomp_3rdm_to_2rdm_sf(d3_copy_0, d1, d2) for t in d3_decomp: coeff = (-1) * sign * (0.5)**n_perm * t.numConstant decomp.append(term(coeff, [], [d1_copy_0] + t.tensors)) # Compute terms from the sum involving two 2-body cumulants for p in range(1): for q in range(p+1,4): ti = [p,q] for i in range(4): if not (i in ti): ti.append(i) for r in range(4): for s in range(r+1,4): if s == p or r == q: bi = [s,r] else: bi = [r,s] temp = range(4) del temp[s] del temp[r] if temp[0] == ti[3] or temp[1] == ti[2]: temp = [temp[1], temp[0]] bi += temp # Determine the number of index permutations n_perm = get_num_perms(ti,bi) # Determine the sign sign = (-1)**n_perm # Create 2RDM 2RDM term bj = [i+4 for i in bi] if n_perm < 2: coeff = sign * (0.5)**n_perm d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])] d2_copy_1 = d2.copy() d2_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] decomp.append(term(coeff, [], [d2_copy_0, d2_copy_1])) elif n_perm == 2: coeff = sign * 0.5 d2_hom_copy_0 = d2_hom.copy() d2_hom_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])] d2_hom_copy_1 = d2_hom.copy() d2_hom_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] decomp.append(term(coeff, [], [d2_hom_copy_0, d2_hom_copy_1])) d2_het_copy_0 = d2_het.copy() d2_het_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])] d2_het_copy_1 = d2_het.copy() d2_het_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] decomp.append(term(coeff, [], [d2_het_copy_0, d2_het_copy_1])) d2_het_copy_0 = d2_het.copy() d2_het_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + [bj[1], bj[0]])] d2_het_copy_1 = d2_het.copy() d2_het_copy_1.indices = [d4.indices[i] for i in (ti[2:4] + [bj[3], bj[2]])] decomp.append(term(coeff, [], [d2_het_copy_0, d2_het_copy_1])) else: raise RuntimeError, "Did not expect a term with %i permutations" %n_perm # Create 'un-permuted' 2RDM 1RDM 1RDM terms coeff = sign * (-1) * (0.5)**n_perm d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])] d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])] decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1])) d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])] decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1])) # Create 'permuted' 2RDM 1RDM 1RDM terms if n_perm < 2: bj = bi[0:2] + bi[3:4] + bi[2:3] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (0.5)**n_perm d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])] d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])] decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1])) bj = bi[1:2] + bi[0:1] + bi[2:4] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (0.5)**n_perm d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])] decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1])) elif n_perm == 2: bj = bi[1:2] + bi[0:1] + bi[3:4] + bi[2:3] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (-1) * (0.5)**n_perm d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[0:2] + bj[0:2])] d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[2:3] + bj[2:3])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[3:4] + bj[3:4])] decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1])) d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])] decomp.append(term(coeff, [], [d2_copy_0, d1_copy_0, d1_copy_1])) else: raise RuntimeError, "Did not expect a term with %i permutations" %n_perm # Create 1RDM 1RDM 1RDM 1RDM terms bj = [i for i in bi] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) bj = bi[0:2] + bi[3:4] + bi[2:3] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (-1) * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) bj = bi[1:2] + bi[0:1] + bi[2:4] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (-1) * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) bj = bi[1:2] + bi[0:1] + bi[3:4] + bi[2:3] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) # Compute terms from the sum involving one 2-body cumulant for p in range(4): for q in range(p+1,4): ti = [p,q] for i in range(4): if not (i in ti): ti.append(i) for r in range(4): for s in range(4): if r == s: continue bi = [r,s] temp = range(4) del temp[max(r,s)] del temp[min(r,s)] if temp[0] == ti[3] or temp[1] == ti[2]: temp = [temp[1], temp[0]] bi += temp # Determine the number of index permutations n_perm = get_num_perms(ti,bi) # Determine the sign sign = (-1)**n_perm # Compute 1RDM 1RDM 2RDM term coeff = 1 bj = [i for i in bi] pairs_rdm1 = [ [ti[i],bj[i]] for i in range(2)] pairs_rdm2 = [ [bj[i],ti[i]] for i in range(2,4)] for pair in pairs_rdm2: if pair[0] == pair[1]: pairs_rdm2 = [] break for pair in pairs_rdm2: if not (pair in pairs_rdm1): #print "swapping bj[2] and bj[3]" (bj[3],bj[2]) = (bj[2],bj[3]) coeff *= -1 break n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff *= sign * (0.5)**n_perm d1_copy_0 = d1.copy() d1_copy_0.indices = [d4.indices[i] for i in (ti[0:1] + bj[0:1])] d1_copy_1 = d1.copy() d1_copy_1.indices = [d4.indices[i] for i in (ti[1:2] + bj[1:2])] d2_copy_0 = d2.copy() d2_copy_0.indices = [d4.indices[i] for i in (ti[2:4] + bj[2:4])] decomp.append(term(coeff, [], [d1_copy_0, d1_copy_1, d2_copy_0])) # Compute 'un-permuted' 1RDM 1RDM 1RDM 1RDM term bj = [i for i in bi] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (-1) * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) # Compute 'permuted' 1RDM 1RDM 1RDM 1RDM term bj = bi[0:2] + bi[3:4] + bi[2:3] n_perm = get_num_perms(ti,bj) bj = [i+4 for i in bj] coeff = sign * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) # Compute terms from the sum involving only one particle density matrices ti = range(4) for bi in makePermutations(4): # Determine the number of permutations n_perm = get_num_perms(ti,bi) # Determine the sign sign = (-1)**n_perm # Compute 1RDM 1RDM 1RDM 1RDM term bj = [i+4 for i in bi] coeff = sign * (0.5)**n_perm d1_copies = [] for j in range(4): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d4.indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) # Combine like terms for t in decomp: t.tensors.sort() decomp.sort() i = 0 while i < len(decomp)-1: if decomp[i].tensors == decomp[i+1].tensors: decomp[i+1].numConstant += decomp[i].numConstant del(decomp[i]) else: i += 1 termChop(decomp) # Return the decomposition return decomp
def decomp_3rdm_to_2rdm_sf(d3, d1, d2, indexOrder = '1212'): """ Returns the list of terms composing the decomposition of the 3 body spin free rdm d3 in terms of one and two body rdms" """ if ( not isinstance(d1, tensor) ) or (len(d1.indices) != 2): raise TypeError, "d1 must be a tensor with 2 indices" if ( not isinstance(d2, tensor) ) or (len(d2.indices) != 4): raise TypeError, "d2 must be a tensor with 4 indices" if ( not isinstance(d3, tensor) ) or (len(d3.indices) != 6): raise TypeError, "d3 must be a tensor with 6 indices" # sort the 3-body RDM's indices into 1212 ordering d3_indices = [] if indexOrder == '1212': d3_indices.extend(d3.indices) elif indexOrder == '1122': for i in range(3): d3_indices.append(d3.indices[2*i]) for i in range(3): d3_indices.append(d3.indices[2*i+1]) else: raise ValueError, "unexpected indexOrder parameter: %s" %(indexOrder) decomp = [] # Prepare terms from sum (-1)^p %gamma^{i_1}_{i_4} %lambda^{i_2`i_3}_{i_5`i_6} for p0 in range(3): temp = range(3) del temp[p0] p1 = temp[0] p2 = temp[1] ti = [p0, p1, p2] for q0 in range(3): temp = range(3) del temp[q0] q1 = temp[0] q2 = temp[1] if q1 == p2 or q2 == p1: bi = [q0, q2, q1] else: bi = [q0, q1, q2] # determine the sign of the term based on the number of index permutations n_perm = get_num_perms(ti,bi) if n_perm > 1: raise ValueError, "n_perm = %i which is > 1. This was unexpected." %n_perm sign = (-1)**n_perm # create the 1RDM 2RDM term bj = [i for i in bi] n_perm = get_num_perms(ti,bj) coeff = sign * (0.5)**n_perm bj = [i+3 for i in bj] d1_copy_0 = d1.copy() d1_copy_0.indices = [d3_indices[i] for i in (ti[0:1] + bj[0:1])] d2_copy_0 = d2.copy() d2_copy_0.indices = [d3_indices[i] for i in (ti[1:3] + bj[1:3])] decomp.append(term(coeff, [], [d1_copy_0, d2_copy_0])) # create the 1RDM 1RDM 1RDM terms bj = [i for i in bi] n_perm = get_num_perms(ti,bj) coeff = sign * (-1) * (0.5)**n_perm bj = [i+3 for i in bj] d1_copies = [] for j in range(3): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) bj = bi[0:1] + bi[2:3] + bi[1:2] n_perm = get_num_perms(ti,bj) coeff = sign * (0.5)**n_perm bj = [i+3 for i in bj] d1_copies = [] for j in range(3): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) # Compute terms from the sum involving only one particle density matrices ti = range(3) for bi in makePermutations(3): # Determine the number of permutations n_perm = get_num_perms(ti,bi) # Determine the sign sign = (-1)**n_perm # Compute 1RDM 1RDM 1RDM 1RDM term bj = [i+3 for i in bi] coeff = sign * (0.5)**n_perm d1_copies = [] for j in range(3): d1_copies.append(d1.copy()) d1_copies[-1].indices = [d3_indices[i] for i in (ti[j:j+1] + bj[j:j+1])] decomp.append(term(coeff, [], d1_copies)) # Combine like terms for t in decomp: t.tensors.sort() decomp.sort() i = 0 while i < len(decomp)-1: if decomp[i].tensors == decomp[i+1].tensors: decomp[i+1].numConstant += decomp[i].numConstant del(decomp[i]) else: i += 1 termChop(decomp) # if original ordering was 1122, convert back to it if indexOrder == '1122': for t in decomp: for ten in t.tensors: if ten.name == d2.name: ten.indices = [ten.indices[0], ten.indices[2], ten.indices[1], ten.indices[3]] # Return the decomposition return decomp
def normalOrder(inTerm): "Returns a list of terms resulting from normal ordering the operators in inTerm." # if options.verbose: # print "converting to normal order: %s" %(str(inTerm)) # check that inTerm is a term if not isinstance(inTerm, term): raise TypeError, "inTerm must be of class term" # determine what types of operators the term contains has_creDesOps = False has_sfExOps = False for t in inTerm.tensors: if isinstance(t, creOp) or isinstance(t, desOp): has_creDesOps = True elif isinstance(t, sfExOp): has_sfExOps = True # If term has both creation/destruction operators and spin free excitation operators, # raise an error if has_creDesOps and has_sfExOps: raise RuntimeError, "Normal ordering not implemented when both creOp/desOp and sfExOp " + \ "tensors are present" # if the term is already normal ordered, return it unchanged elif inTerm.isNormalOrdered(): outTerms = [inTerm.copy()] # Normal ordering for creOp/desOp elif has_creDesOps: # Separate the cre/des operators from other tensors ops = [] nonOps = [] for t in inTerm.tensors: if isinstance(t, creOp) or isinstance(t, desOp): ops.append(t.copy()) else: nonOps.append(t.copy()) # Generate all contraction pairs contractionPairs = [] for i in range(len(ops)): iTerm = ops[i] for j in range(i+1,len(ops)): jTerm = ops[j] if isinstance(iTerm, desOp) and isinstance(jTerm, creOp): contractionPairs.append((i,j)); #print "contractionPairs\n", contractionPairs # Determine maximum contraction order creCount = 0 maxConOrder = 0 for i in range(len(ops)-1,-1,-1): iTerm = ops[i] if isinstance(iTerm, creOp): creCount +=1 elif isinstance(iTerm, desOp) and creCount > 0: maxConOrder += 1 creCount -= 1 del(creCount,iTerm) # Generate all contractions contractions = [] for i in range(maxConOrder+1): subCons = makeTuples(i,contractionPairs) j = 0 while j < len(subCons): creOpTags = [] desOpTags = [] for k in range(i): creOpTags.append(subCons[j][k][1]) desOpTags.append(subCons[j][k][0]) if allDifferent(creOpTags) and allDifferent(desOpTags): j += 1 else: del(subCons[j]) for j in range(len(subCons)): contractions.append(subCons[j]) del(subCons,creOpTags,desOpTags,contractionPairs) #print "contractions:\n", contractions # For each contraction, generate the resulting term outTerms = [] for contraction in contractions: conSign = 1 deltaFuncs = [] conIndeces = [] subOpString = [] subOpString.extend(ops) for conPair in contraction: index1 = ops[conPair[0]].indices[0] index2 = ops[conPair[1]].indices[0] deltaFuncs.append(kroneckerDelta([index1,index2])) subOpString[conPair[0]] = 'contracted' subOpString[conPair[1]] = 'contracted' for q in subOpString[conPair[0]+1:conPair[1]]: if not (q is 'contracted'): conSign *= -1 i = 0 while i < len(subOpString): if subOpString[i] is 'contracted': del(subOpString[i]) else: i += 1 (sortSign,sortedOps) = sortOps(subOpString) totalSign = conSign * sortSign outTensors = [] outTensors.extend(nonOps) outTensors.extend(deltaFuncs) outTensors.extend(sortedOps) outTerms.append( term(totalSign * inTerm.numConstant, inTerm.constants, outTensors) ) # Normal ordering for sfExOps elif has_sfExOps: # Make separate lists of the spin free excitation operators and other tensors sfExOp_list = [] other_list = [] for t in inTerm.tensors: if isinstance(t, sfExOp): sfExOp_list.append(t.copy()) else: other_list.append(t.copy()) # Initialize n, the number of remaining spin free excitation operators n = len(sfExOp_list) # Set the original term, with all excitation operators moved to the end, as the # first iteration's input term iter_input_terms = [term(inTerm.numConstant, inTerm.constants, other_list + sfExOp_list)] # Successively normal order the last two excitation operators until each term # has only one exitation operator left (at which point the term is normal ordered) while n > 1: # Initialize the list to hold this iteration's output terms iter_output_terms = [] # For each of this iteration's input terms, produce all terms resulting from normal ordering # the last two excitation operators for t in iter_input_terms: # Make a list of the term's tensors that excludes the last two excitation operators tensors_except_last_two = [] tensors_except_last_two.extend(t.tensors[0:-2]) # Give short names for the last two excitation operators and their orders e1 = t.tensors[-2] e2 = t.tensors[-1] o1 = e1.order o2 = e2.order # Loop over the number of contractions for nc in range(min(o1,o2)+1): # Compute the order of excitation operator for the current number of contractions newOrder = o1 + o2 - nc # Compute all nc-tuples of index numbers from e1 and e2, as well as all permutations of # the order in which the tuples may be combined to form a contraction perms = [0] if nc > 0: perms = makePermutations(nc) tups1 = makeTuples(nc, range(o1)) tups2 = makeTuples(nc, range(o2)) # For each contraction, compute the resulting term for perm in perms: for tup1 in tups1: for tup2 in tups2: # Initialize the term's tensor list tensorList = [] tensorList.extend(tensors_except_last_two) # Compute the pairs of indices to be contracted # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair. conPairs = [[],[]] for i in range(nc): conPairs[0].append(tup1[perm[i]]) conPairs[1].append(tup2[i]) # Initialize the index list for the new excitation operator indexList = [False] * (2*newOrder) # Populate the index list for the new excitation operator # Also, create a kronecker delta function for each contraction pair for i in range(o1): indexList[i] = e1.indices[i] # The usual one if i in conPairs[0]: i1 = i+o1 i2 = conPairs[1][conPairs[0].index(i)] ind1 = e1.indices[i1] ind2 = e2.indices[i2] tensorList.insert(0, kroneckerDelta([ind1,ind2])) indexList[i+newOrder] = e2.indices[o2+i2] else: indexList[i+newOrder] = e1.indices[i+o1] # Put the usual counterpart into indexList count = 0 # in case of that e1.indices[i] is not contracted for i in range(o2): if not (i in conPairs[1]): indexList[o1+count] = e2.indices[i] indexList[o1+count+newOrder] = e2.indices[i+o2] count += 1 # Ensure that all slots in the index list have been filled for ind in indexList: if ind is False: raise RuntimeError, "There is at least one unassigned index in the new spin free operator." # Add the new excitation operator to the tensor list tensorList.append(sfExOp(indexList)) # Add the resulting term to this iteration's list of output terms iter_output_terms.append(term(inTerm.numConstant, inTerm.constants, tensorList)) # Set this iteration's list of output terms as the next iteration's input terms iter_input_terms = iter_output_terms # Decrement the counter for the number of excitation operators n -= 1; #print "MADE OK!!!" # Set the return value as the final iteration's output terms outTerms = iter_output_terms else: raise RuntimeError, "Normal ordering function failed to choose what to do." # print "Terms after normal ordering:" # for t in outTerms: # print t return outTerms
def contractCoreOps_sf(inTerm): "Returns a list of terms resulting from normal ordering the operators in inTerm." # check that inTerm is a term if not isinstance(inTerm, term): raise TypeError, "inTerm must be of class term" # Make separate lists of the spin free excitation operators and other tensors sfExOp_list = [] other_list = [] for t in inTerm.tensors: if isinstance(t, sfExOp): sfExOp_list.append(t.copy()) else: other_list.append(t.copy()) # Initialize n, the number of remaining spin free excitation operators if len(sfExOp_list) != 1: raise RuntimeError, "terms should have single sfExOp" # Give short names for the excitation operator and their order e1 = sfExOp_list[0] o1 = e1.order # list of offset of "core" type indices cIndsCre = [] cIndsDes = [] for i in range(o1): if e1.indices[i].indType == (options.core_type,): cIndsCre.append(i) if e1.indices[i+o1].indType == (options.core_type,): cIndsDes.append(i+o1) # all the "core" type indices should be contracted nc = len(cIndsCre) outTerms = [] if nc != len(cIndsDes): outTerms.append(term(0.0, [], [])) return outTerms if nc == 0: outTerms = [inTerm.copy()] return outTerms newOrder = o1 - nc # For each contraction, compute the resulting term perms = makePermutations(nc) for perm in perms: # Compute the pairs of indices to be contracted # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair. conPairs = [[],[]] for i in range(nc): conPairs[0].append(cIndsCre[perm[i]]) conPairs[1].append(cIndsDes[i]) # evaluate constant prefactor prefactor = 1 inds = [] for i in range(nc): if i not in inds: inds.append(i) i0 = conPairs[0][i] i1 = conPairs[1][i] - o1 if i1 == i0: prefactor *= 2 continue #old if i1 in conPairs[0]: #old inds.append(conPairs[0].index(i1)) #old i2 = conPairs[1][conPairs[0].index(i1)] - o1 #old if i2 == i0: #old prefactor *= 2 #old continue #old if i2 in conPairs[0]: #old inds.append(conPairs[0].index(i2)) #old i3 = conPairs[1][conPairs[0].index(i2)] - o1 #old if i3 == i0: #old prefactor *= 2 #old continue #old if i3 in conPairs[0]: #old inds.append(conPairs[0].index(i3)) #old i4 = conPairs[1][conPairs[0].index(i3)] - o1 #old if i4 == i0: #old prefactor *= 2 #old continue #old else: #old raise RuntimeError, "NYI:recursive algorithm1" while i1 in conPairs[0]: inds.append(conPairs[0].index(i1)) i1 = conPairs[1][conPairs[0].index(i1)] - o1 if i1 == i0: prefactor *= 2 break # # Initialize the term's tensor list tensorList = other_list[:] # Initialize the index list for the new excitation operator indexList = [False] * (2*newOrder) # Populate the index list for the new excitation operator # Also, create a kronecker delta function for each contraction pair count = 0 for i1 in range(o1): if i1 in conPairs[0]:#create a kronecker delta i2 = conPairs[1][conPairs[0].index(i1)] ind1 = e1.indices[i1] ind2 = e1.indices[i2] tensorList.insert(0, kroneckerDelta([ind1,ind2])) else: #insert a pair of indices indexList[count] = e1.indices[i1] i2 = i1+o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old raise RuntimeError, "NYI:recursive algorithm2" #old else: #old indexList[count+newOrder] = e1.indices[i2] #old else: #old indexList[count+newOrder] = e1.indices[i2] #old else: #old indexList[count+newOrder] = e1.indices[i2] #old else: #old indexList[count+newOrder] = e1.indices[i2] while i2 in conPairs[1]: i2 = conPairs[0][conPairs[1].index(i2)] + o1 else: indexList[count+newOrder] = e1.indices[i2] # count += 1 # Ensure that all slots in the index list have been filled for ind in indexList: if ind is False: raise RuntimeError, "There is at least one unassigned index in the new spin free operator." # Add the new excitation operator to the tensor list if len(indexList) != 0: tensorList.append(sfExOp(indexList)) # Determine the sign indPairs = [[],[]] for i in range(o1): indPairs[0].append(i) if i in conPairs[0]: indPairs[1].append(conPairs[1][conPairs[0].index(i)]-o1) else: a = e1.indices[i] b = indexList[indexList.index(a)+newOrder] #indPairs[1].append(e1.indices.index(b)-o1)#is not correct when there are duplicate indices indPairs[1].append(e1.indices[o1:2*o1].index(b)) #for i in range(o1): indPairs[1].index(i) n_perm = get_num_perms(indPairs[0], indPairs[1]) sign = (-1)**n_perm # Add the resulting term to this iteration's list of output terms #print term(sign*prefactor*inTerm.numConstant, inTerm.constants, tensorList) #print "-------------------------------------------------------------------" outTerms.append(term(sign*prefactor*inTerm.numConstant, inTerm.constants, tensorList)) return outTerms
def contractCoreOps_sf(inTerm): "Returns a list of terms resulting from normal ordering the operators in inTerm." # check that inTerm is a term if not isinstance(inTerm, term): raise TypeError, "inTerm must be of class term" # Make separate lists of the spin free excitation operators and other tensors sfExOp_list = [] other_list = [] for t in inTerm.tensors: if isinstance(t, sfExOp): sfExOp_list.append(t.copy()) else: other_list.append(t.copy()) # Initialize n, the number of remaining spin free excitation operators if len(sfExOp_list) != 1: raise RuntimeError, "terms should have single sfExOp" # Give short names for the excitation operator and their order e1 = sfExOp_list[0] o1 = e1.order # list of offset of "core" type indices cIndsCre = [] cIndsDes = [] for i in range(o1): if e1.indices[i].indType == (options.core_type, ): cIndsCre.append(i) if e1.indices[i + o1].indType == (options.core_type, ): cIndsDes.append(i + o1) # all the "core" type indices should be contracted nc = len(cIndsCre) outTerms = [] if nc != len(cIndsDes): outTerms.append(term(0.0, [], [])) return outTerms if nc == 0: outTerms = [inTerm.copy()] return outTerms newOrder = o1 - nc # For each contraction, compute the resulting term perms = makePermutations(nc) for perm in perms: # Compute the pairs of indices to be contracted # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair. conPairs = [[], []] for i in range(nc): conPairs[0].append(cIndsCre[perm[i]]) conPairs[1].append(cIndsDes[i]) # evaluate constant prefactor prefactor = 1 inds = [] for i in range(nc): if i not in inds: inds.append(i) i0 = conPairs[0][i] i1 = conPairs[1][i] - o1 if i1 == i0: prefactor *= 2 continue #old if i1 in conPairs[0]: #old inds.append(conPairs[0].index(i1)) #old i2 = conPairs[1][conPairs[0].index(i1)] - o1 #old if i2 == i0: #old prefactor *= 2 #old continue #old if i2 in conPairs[0]: #old inds.append(conPairs[0].index(i2)) #old i3 = conPairs[1][conPairs[0].index(i2)] - o1 #old if i3 == i0: #old prefactor *= 2 #old continue #old if i3 in conPairs[0]: #old inds.append(conPairs[0].index(i3)) #old i4 = conPairs[1][conPairs[0].index(i3)] - o1 #old if i4 == i0: #old prefactor *= 2 #old continue #old else: #old raise RuntimeError, "NYI:recursive algorithm1" while i1 in conPairs[0]: inds.append(conPairs[0].index(i1)) i1 = conPairs[1][conPairs[0].index(i1)] - o1 if i1 == i0: prefactor *= 2 break # # Initialize the term's tensor list tensorList = other_list[:] # Initialize the index list for the new excitation operator indexList = [False] * (2 * newOrder) # Populate the index list for the new excitation operator # Also, create a kronecker delta function for each contraction pair count = 0 for i1 in range(o1): if i1 in conPairs[0]: #create a kronecker delta i2 = conPairs[1][conPairs[0].index(i1)] ind1 = e1.indices[i1] ind2 = e1.indices[i2] tensorList.insert(0, kroneckerDelta([ind1, ind2])) else: #insert a pair of indices indexList[count] = e1.indices[i1] i2 = i1 + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old i2 = conPairs[0][conPairs[1].index(i2)] + o1 #old if i2 in conPairs[1]: #old raise RuntimeError, "NYI:recursive algorithm2" #old else: #old indexList[count+newOrder] = e1.indices[i2] #old else: #old indexList[count+newOrder] = e1.indices[i2] #old else: #old indexList[count+newOrder] = e1.indices[i2] #old else: #old indexList[count+newOrder] = e1.indices[i2] while i2 in conPairs[1]: i2 = conPairs[0][conPairs[1].index(i2)] + o1 else: indexList[count + newOrder] = e1.indices[i2] # count += 1 # Ensure that all slots in the index list have been filled for ind in indexList: if ind is False: raise RuntimeError, "There is at least one unassigned index in the new spin free operator." # Add the new excitation operator to the tensor list if len(indexList) != 0: tensorList.append(sfExOp(indexList)) # Determine the sign indPairs = [[], []] for i in range(o1): indPairs[0].append(i) if i in conPairs[0]: indPairs[1].append(conPairs[1][conPairs[0].index(i)] - o1) else: a = e1.indices[i] b = indexList[indexList.index(a) + newOrder] #indPairs[1].append(e1.indices.index(b)-o1)#is not correct when there are duplicate indices indPairs[1].append(e1.indices[o1:2 * o1].index(b)) #for i in range(o1): indPairs[1].index(i) n_perm = get_num_perms(indPairs[0], indPairs[1]) sign = (-1)**n_perm # Add the resulting term to this iteration's list of output terms #print term(sign*prefactor*inTerm.numConstant, inTerm.constants, tensorList) #print "-------------------------------------------------------------------" outTerms.append( term(sign * prefactor * inTerm.numConstant, inTerm.constants, tensorList)) return outTerms
def normalOrder(inTerm): "Returns a list of terms resulting from normal ordering the operators in inTerm." # if options.verbose: # print "converting to normal order: %s" %(str(inTerm)) # check that inTerm is a term if not isinstance(inTerm, term): raise TypeError, "inTerm must be of class term" # determine what types of operators the term contains has_creDesOps = False has_sfExOps = False for t in inTerm.tensors: if isinstance(t, creOp) or isinstance(t, desOp): has_creDesOps = True elif isinstance(t, sfExOp): has_sfExOps = True # If term has both creation/destruction operators and spin free excitation operators, # raise an error if has_creDesOps and has_sfExOps: raise RuntimeError, "Normal ordering not implemented when both creOp/desOp and sfExOp " + \ "tensors are present" # if the term is already normal ordered, return it unchanged elif inTerm.isNormalOrdered(): outTerms = [inTerm.copy()] # Normal ordering for creOp/desOp elif has_creDesOps: # Separate the cre/des operators from other tensors ops = [] nonOps = [] for t in inTerm.tensors: if isinstance(t, creOp) or isinstance(t, desOp): ops.append(t.copy()) else: nonOps.append(t.copy()) # Generate all contraction pairs contractionPairs = [] for i in range(len(ops)): iTerm = ops[i] for j in range(i + 1, len(ops)): jTerm = ops[j] if isinstance(iTerm, desOp) and isinstance(jTerm, creOp): contractionPairs.append((i, j)) #print "contractionPairs\n", contractionPairs # Determine maximum contraction order creCount = 0 maxConOrder = 0 for i in range(len(ops) - 1, -1, -1): iTerm = ops[i] if isinstance(iTerm, creOp): creCount += 1 elif isinstance(iTerm, desOp) and creCount > 0: maxConOrder += 1 creCount -= 1 del (creCount, iTerm) # Generate all contractions contractions = [] for i in range(maxConOrder + 1): subCons = makeTuples(i, contractionPairs) j = 0 while j < len(subCons): creOpTags = [] desOpTags = [] for k in range(i): creOpTags.append(subCons[j][k][1]) desOpTags.append(subCons[j][k][0]) if allDifferent(creOpTags) and allDifferent(desOpTags): j += 1 else: del (subCons[j]) for j in range(len(subCons)): contractions.append(subCons[j]) del (subCons, creOpTags, desOpTags, contractionPairs) #print "contractions:\n", contractions # For each contraction, generate the resulting term outTerms = [] for contraction in contractions: conSign = 1 deltaFuncs = [] conIndeces = [] subOpString = [] subOpString.extend(ops) for conPair in contraction: index1 = ops[conPair[0]].indices[0] index2 = ops[conPair[1]].indices[0] deltaFuncs.append(kroneckerDelta([index1, index2])) subOpString[conPair[0]] = 'contracted' subOpString[conPair[1]] = 'contracted' for q in subOpString[conPair[0] + 1:conPair[1]]: if not (q is 'contracted'): conSign *= -1 i = 0 while i < len(subOpString): if subOpString[i] is 'contracted': del (subOpString[i]) else: i += 1 (sortSign, sortedOps) = sortOps(subOpString) totalSign = conSign * sortSign outTensors = [] outTensors.extend(nonOps) outTensors.extend(deltaFuncs) outTensors.extend(sortedOps) outTerms.append( term(totalSign * inTerm.numConstant, inTerm.constants, outTensors)) # Normal ordering for sfExOps elif has_sfExOps: # Make separate lists of the spin free excitation operators and other tensors sfExOp_list = [] other_list = [] for t in inTerm.tensors: if isinstance(t, sfExOp): sfExOp_list.append(t.copy()) else: other_list.append(t.copy()) # Initialize n, the number of remaining spin free excitation operators n = len(sfExOp_list) # Set the original term, with all excitation operators moved to the end, as the # first iteration's input term iter_input_terms = [ term(inTerm.numConstant, inTerm.constants, other_list + sfExOp_list) ] # Successively normal order the last two excitation operators until each term # has only one exitation operator left (at which point the term is normal ordered) while n > 1: # Initialize the list to hold this iteration's output terms iter_output_terms = [] # For each of this iteration's input terms, produce all terms resulting from normal ordering # the last two excitation operators for t in iter_input_terms: # Make a list of the term's tensors that excludes the last two excitation operators tensors_except_last_two = [] tensors_except_last_two.extend(t.tensors[0:-2]) # Give short names for the last two excitation operators and their orders e1 = t.tensors[-2] e2 = t.tensors[-1] o1 = e1.order o2 = e2.order #lsh spin s1 = e1.spin s2 = e2.spin spin1f = s1[0][:] spin1e = s1[1][:] spin2f = s2[0][:] spin2e = s2[1][:] shift = max(max(spin1f), max(spin1e)) # Loop over the number of contractions for nc in range(min(o1, o2) + 1): # Compute the order of excitation operator for the current number of contractions newOrder = o1 + o2 - nc # Compute all nc-tuples of index numbers from e1 and e2, as well as all permutations of # the order in which the tuples may be combined to form a contraction perms = [0] if nc > 0: perms = makePermutations(nc) tups1 = makeTuples(nc, range(o1)) tups2 = makeTuples(nc, range(o2)) #print('perms=',perms) #print('tups1=',tups1) #print('tups2=',tups2) # For each contraction, compute the resulting term for perm in perms: for tup1 in tups1: for tup2 in tups2: # Initialize the term's tensor list tensorList = [] tensorList.extend(tensors_except_last_two) # Compute the pairs of indices to be contracted # Example: (conPairs[0][p], conPairs[1][p]) is the pth contraction pair. conPairs = [[], []] for i in range(nc): conPairs[0].append(tup1[perm[i]]) conPairs[1].append(tup2[i]) # Initialize the index list for the new excitation operator indexList = [False] * (2 * newOrder) # Populate the index list for the new excitation operator # Also, create a kronecker delta function for each contraction pair for i in range(o1): indexList[i] = e1.indices[ i] # The usual one if i in conPairs[0]: i1 = i + o1 i2 = conPairs[1][conPairs[0].index(i)] ind1 = e1.indices[i1] ind2 = e2.indices[i2] tensorList.insert( 0, kroneckerDelta([ind1, ind2])) indexList[i + newOrder] = e2.indices[o2 + i2] else: indexList[i + newOrder] = e1.indices[ i + o1] # Put the usual counterpart into indexList count = 0 # in case of that e1.indices[i] is not contracted for i in range(o2): if not (i in conPairs[1]): indexList[o1 + count] = e2.indices[i] indexList[o1 + count + newOrder] = e2.indices[i + o2] count += 1 #lsh parity test #print 'conPairs=', conPairs parity_n = 0 o1_l = [i for i in range(o1)] o2_l = [i for i in range(o2)] for i in range(len(conPairs[0])): p1 = conPairs[0][i] p2 = conPairs[1][i] parity_n += o1_l.index(p1) #print 'parity = ', p1, p2, o1_l, o2_l, parity_n parity_n += o2_l.index(p2) #print 'parity = ', p1, p2, o1_l, o2_l, parity_n o1_l.remove(p1) o2_l.remove(p2) parity = (-1)**(parity_n) #parity *= 2**(len(conPairs[0])) #lsh spin test spin_dict = {} for i in range(len(conPairs[0])): p1 = conPairs[0][i] p2 = conPairs[1][i] spin_dict[spin2f[p2]] = spin1e[p1] spin_new_f = spin1f[:] #print 'spin 1 = ', spin_new_f for i in range(len(spin2f)): if i not in conPairs[1]: spin_new_f.append(spin2f[i] + shift + 1) #print 'spin 2 = ', spin_new_f spin_new_e = [] for i in range(len(spin1e)): if i not in conPairs[0]: spin_new_e.append(spin1e[i]) #print 'spin 3 = ', spin_new_e for i in spin2e: if i not in spin_dict: spin_new_e.append(i + shift + 1) else: spin_new_e.append(spin_dict[i]) #print 'spin 4 = ', spin_new_e # Ensure that all slots in the index list have been filled for ind in indexList: if ind is False: raise RuntimeError, "There is at least one unassigned index in the new spin free operator." #print 'spin= ', spin_new_f, spin_new_e # Add the new excitation operator to the tensor list tensorList.append( sfExOp(indexList, spin=[spin_new_f, spin_new_e])) # Add the resulting term to this iteration's list of output terms iter_output_terms.append( term(parity * inTerm.numConstant, inTerm.constants, tensorList)) # Set this iteration's list of output terms as the next iteration's input terms iter_input_terms = iter_output_terms # Decrement the counter for the number of excitation operators n -= 1 #print "MADE OK!!!" # Set the return value as the final iteration's output terms outTerms = iter_output_terms else: raise RuntimeError, "Normal ordering function failed to choose what to do." # print "Terms after normal ordering:" # for t in outTerms: # print t return outTerms