def _get_fermi_partitions(term: Term, spec: _AGPFSpec): """Given a term with a list of fermionic vectors, extract the various partitions of N, Pdag and P kind of terms. Assumes: All possible simplification and Pairing / SU2 extraction has been performed, i.e. all the indices are distinct. """ vecs = term.vecs amp = term.amp fermi_indcs = [] cr = spec.c_dag.base an = spec.c_.base # First extract all the indices of fermion operators for v in vecs: if v.base in (cr, an): fermi_indcs.append(v.indices[1]) if len(fermi_indcs) == 0: # if there are no fermion operators, return the term as it is return [Term(sums=term.sums, amp=amp, vecs=vecs)] else: # if there are fermion operators, then get all the possible # partitions of indices deltas_partns = list(_generate_partitions(fermi_indcs)) # get a list of even partitions, i.e. throw away partitions involving # odd number of indices evens = [ all( len(deltas_partns[i][j]) % 2 == 0 for j in range(len(deltas_partns[i]))) for i in range(len(deltas_partns)) ] # Now iterate through the list of partitions and form the deltas expression # for the amplitude delta_amp = Integer(0) for i in range(len(deltas_partns)): # if statement for even partitions if evens[i]: pt = deltas_partns[i] else: continue # Form the delta expressions delta_intmd = Integer(1) for i in range(len(pt)): delta_intmd *= _construct_deltas(pt[i]) if i > 0: delta_intmd *= (1 - KroneckerDelta(pt[i][0], pt[i - 1][0])) delta_amp += delta_intmd new_amp = amp * delta_amp return [Term(sums=term.sums, amp=new_amp, vecs=vecs)]
def get_vev_of_term(term): """Return the vev mapping of the term""" vecs = term.vecs t_amp = term.amp pdag_cnt = 0 p_cnt = 0 n_cnt = 0 pdag_indlist = [] p_indlist = [] n_indlist = [] if len(vecs) == 0: return [Term(sums=term.sums, amp=t_amp, vecs=vecs)] for i in vecs: if i.base == pdag_: pdag_cnt += 1 pdag_indlist.extend(list(i.indices)) elif i.base == p_: p_cnt += 1 p_indlist.extend(list(i.indices)) elif i.base == num_: n_cnt += 1 n_indlist.extend(list(i.indices)) else: return [] if pdag_cnt != p_cnt: # The expression must have equal number of Pdag's and P_'s return [] elif len(pdag_indlist) != len(set(pdag_indlist)): return [] elif len(p_indlist) != len(set(p_indlist)): return [] else: # Combining all the indices indcs = pdag_indlist indcs.extend(n_indlist) indcs.extend(p_indlist) # Counting the different number of indices in order # to select the right symbol 'asymb' idx1 = int(n_cnt) idx_ppdag = len(indcs) - n_cnt idx2 = int((idx_ppdag / 2)) asymb = zlist[idx1][idx2] t_amp = t_amp * asymb[indcs] return [Term(sums=term.sums, amp=t_amp, vecs=())]
def _isAGP(self, term): """Returns True if the vectors in the term are generators of the AGP algebra, otherwise False""" vecs = term.vecs for v in vecs: if v.base not in (self.cartan, self.raise_, self.lower): raise ValueError('Unexpected generator of the AGP Algebra', v) return [Term(sums=term.sums, amp=term.amp, vecs=term.vecs)]
def _isUGA(self,term): """Function returns True if the vectors are uga generators, otherwise False""" vecs = term.vecs for v in vecs: if v.base!=self.uga: raise ValueError('Unexpected generator of the Unitary Group',v) return [Term(sums=term.sums, amp=term.amp, vecs=term.vecs)]
def _subst_actv_vec_rdm (term: Term, rdm: IndexedBase, actv_range, op_parser, resolvers): parsed = [op_parser (vec, term) for vec in term.vecs] ranges = [try_resolve_range (p[2][0], dict(term.sums), resolvers.value) for p in parsed] inac_vecs = [vec for r, vec in zip (ranges, term.vecs) if not r==actv_range] actv_parsed = [p for r, p in zip (ranges, parsed) if r==actv_range] amp = term.amp if len (actv_parsed) > 0: actv_cr_idxs = sum([[p[2][0]] for p in actv_parsed if p[1]==CR], []) actv_an_idxs = sum([[p[2][0]] for p in reversed (actv_parsed) if p[1]==AN], []) actv_idxs = actv_cr_idxs + actv_an_idxs amp = amp * rdm[actv_idxs] return Term (term.sums, amp, inac_vecs)
def _canonicalize_indices(term: Term, spec: _AGPFSpec): """Here, we canonicalize the free indices in the tensor expressions - that is replace the higher key indices with lower key ones everywhere """ # get the new term and substs new_amp, substs = _try_simpl_unresolved_deltas(term.amp) # check for overlap in the dlists and unique_indices # if any of the maps contain two indices that are supposed to be unique, # it should return zero/empty term. unique_list = spec.unique_ind # New substitutions based on the chain of delta new_substs = {} if substs: dlists = _delta_map(substs) for s1 in dlists: s1_sorted = list(ordered(s1)) j = 1 while j < len(s1_sorted): new_substs[s1_sorted[j]] = s1_sorted[0] j += 1 if not unique_list: continue for s2 in unique_list: if len(list(s1 & s2)) > 1: return [] else: continue # construct the new term new_term = term.subst(new_substs) return [Term(sums=new_term.sums, amp=new_amp, vecs=new_term.vecs)]
def vev_of_term(term): """Return the VEV of a given term""" vecs = term.vecs t_amp = term.amp ind_list = [] if len(vecs) == 0: return term elif all(v.base == ctan for v in vecs): for i in vecs: if set(i.indices).issubset(set(ind_list)): t_amp = t_amp * 2 # NOTE: This is only true when evaluating expectation over AGP, # or any other seniority zero state else: ind_list.extend(list(i.indices)) else: return [] t_amp = t_amp * gam[ind_list] return [Term(sums=term.sums, amp=t_amp, vecs=())]
def get_vev_of_term(term): """Return the vev mapping of the term""" vecs = term.vecs t_amp = term.amp pdag_cnt = 0 p_cnt = 0 n_cnt = 0 pdag_indlist = [] p_indlist = [] n_indlist = [] if len(vecs) == 0: return [ Term(sums=term.sums, amp=t_amp * zlist[0][0], vecs=vecs) ] # Classify the indices into pdag, n, and p indicies for i in vecs: if i.base == pdag_: pdag_indlist.extend(list(i.indices)) elif i.base == p_: p_indlist.extend(list(i.indices)) elif i.base == num_: n_indlist.extend(list(i.indices)) else: return [] # Count the number of indices pdag_cnt = len(pdag_indlist) p_cnt = len(p_indlist) # First, we extract only the unique N indices unique_n_indlist = list(set(n_indlist)) n_cnt = len(unique_n_indlist) # Introduce appropriate power of 2 in amplitude to include the effect # of unique indices only being considered in N ldiff = len(n_indlist) - len(unique_n_indlist) t_amp *= 2**(ldiff) if pdag_cnt != p_cnt: # The expression must have equal number of Pdag's and P_'s return [] elif len(pdag_indlist) != len(set(pdag_indlist)): return [] elif len(p_indlist) != len(set(p_indlist)): return [] else: # Combining all the indices indcs = pdag_indlist indcs.extend(unique_n_indlist) indcs.extend(p_indlist) # Counting the different number of indices in order # to select the right symbol 'asymb' idx1 = int(n_cnt) idx_ppdag = len(indcs) - n_cnt idx2 = int((idx_ppdag / 2)) asymb = zlist[idx1][idx2] t_amp = t_amp * asymb[indcs] return [Term(sums=term.sums, amp=t_amp, vecs=())]
def _get_su2_vecs(term: Term, spec: _AGPFSpec): """Given a term with a list of vectors, extract the obvious BCS vectors NOTE: This bind function assumes that the term has already been simplified NOTE2: We just ignore the SP and SM terms in extracting su2 """ vecs = term.vecs amp = term.amp int_vecs = [] new_vecs = [] Pdag = spec.Pdag P = spec.P N = spec.N N_up = spec.Nup N_dn = spec.Ndn SP = spec.S_p SM = spec.S_m SZ = spec.S_z cr = (spec.c_dag.base, spec.c_dag.indices[0]) an = (spec.c_.base, spec.c_.indices[0]) # Get rid of the trivial case where no SU2 vector is possible. if len(vecs) <= 1: new_vecs = vecs return [Term(sums=term.sums, amp=amp, vecs=new_vecs)] # Now onto the more involved situations: # First, let us extract all the number and pair-annihilation operators i = 0 while i < (len(vecs) - 1): v1 = (vecs[i].base, vecs[i].indices[0]) v2 = (vecs[i + 1].base, vecs[i + 1].indices[0]) if (v1 == cr): if (v2 == an): if (vecs[i].indices[1:] == vecs[i + 1].indices[1:]): if vecs[i].indices[2] == SpinOneHalf.UP: int_vecs.append(N_up[vecs[i].indices[1]]) elif vecs[i].indices[2] == SpinOneHalf.DOWN: int_vecs.append(N_dn[vecs[i].indices[1]]) i += 2 continue # elif (vecs[i].indices[1] == vecs[i+1].indices[1]): # if vecs[i].indices[2] == SpinOneHalf.UP: # int_vecs.append(SP[vecs[i].indices[1]]) # i += 2 # continue # else: # int_vecs.append(SM[vecs[i].indices[1]]) # i += 2 # continue else: int_vecs.append(vecs[i]) i += 1 continue elif (v2 == cr): # Do not consider the pair-creation operators yet int_vecs.append(vecs[i]) i += 1 continue else: raise ValueError('Input term is not simplified') elif (v1 == an): if (v2 == an): if (vecs[i].indices[1] == vecs[i + 1].indices[1]): # if both annihilation, and have same lattice index, # then assuming the term is simplified, the spins # must be opposite (first one would be DOWN) int_vecs.append(P[vecs[i].indices[1]]) i += 2 continue else: int_vecs.append(vecs[i]) i += 1 continue else: int_vecs.append(vecs[i]) i += 1 continue else: int_vecs.append(vecs[i]) i += 1 continue # If the last two operators did not map, we are left with # the last fermion operator if i == len(vecs) - 1: int_vecs.append(vecs[i]) # Now map all the pair-creation operators i = 0 while i < (len(int_vecs) - 1): v1 = (int_vecs[i].base, int_vecs[i].indices[0]) v2 = (int_vecs[i + 1].base, int_vecs[i + 1].indices[0]) if (v1 == cr): if (v2 == cr): if (int_vecs[i].indices[1] == int_vecs[i + 1].indices[1]): # if both creation, and have same lattice index, # then assuming term is simplified, the spins must be # opposite. new_vecs.append(Pdag[int_vecs[i].indices[1]]) i += 2 continue else: new_vecs.append(int_vecs[i]) i += 1 continue else: new_vecs.append(int_vecs[i]) i += 1 continue # raise ValueError('Input term is not simplified') else: new_vecs.append(int_vecs[i]) i += 1 continue # If the last two operators did not map, we are left with # the last fermion operator if i == len(int_vecs) - 1: new_vecs.append(int_vecs[i]) return [Term(sums=term.sums, amp=amp, vecs=new_vecs)]