def d1_q1_mapping(dim): """ Map the ck to kc D1 + Q1 = I :param dim: linear dimension of the 1-RDM :return: the dual basis of the mapping :rtype: DualBasis """ db = DualBasis() dbe_list = [] for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() if i != j: dbe.add_element('ck', (i, j), 0.5) dbe.add_element('ck', (j, i), 0.5) dbe.add_element('kc', (j, i), 0.5) dbe.add_element('kc', (i, j), 0.5) dbe.dual_scalar = 0.0 else: dbe.add_element('ck', (i, j), 1.0) dbe.add_element('kc', (i, j), 1.0) dbe.dual_scalar = 1.0 # db += dbe dbe_list.append(dbe) return DualBasis(elements=dbe_list) # db
def d2_d1_mapping(dim, normalization): """ Construct dual basis for contracting d2 -> d1 :param dim: linear dimension of the 1-RDM :param normalization: normalization constant for coeff of D1 elememnts :return: the dual basis of the contraction :rtype: DualBasis """ db_basis = DualBasis() dbe_list = [] for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() for r in range(dim): # duplicate entries get summed in DualBasisElement dbe.add_element('cckk', (i, r, j, r), 0.5) dbe.add_element('cckk', (j, r, i, r), 0.5) # D1 terms dbe.add_element('ck', (i, j), -0.5 * normalization) dbe.add_element('ck', (j, i), -0.5 * normalization) dbe.simplify() # db_basis += dbe dbe_list.append(dbe) return DualBasis(elements=dbe_list) # db_basis
def d2_q2_mapping(dim): """ Map each d2 block to the q2 block :param dim: rank of spatial single-particle basis :return: """ krond = np.eye(dim) def d2q2element(p, q, r, s, factor, tname_d1_1, tname_d1_2, tname_d2, tname_q2): dbe = DualBasisElement() dbe.add_element(tname_d1_1, (p, r), 2.0 * krond[q, s] * factor) dbe.add_element(tname_d1_2, (q, s), 2.0 * krond[p, r] * factor) dbe.add_element(tname_d1_1, (p, s), -2.0 * krond[r, q] * factor) dbe.add_element(tname_d1_2, (q, r), -2.0 * krond[p, s] * factor) dbe.add_element(tname_q2, (r, s, p, q), 1.0 * factor) dbe.add_element(tname_d2, (p, q, r, s), -1.0 * factor) # remember the negative sign because AX = b dbe.dual_scalar = -2.0 * krond[s, p] * krond[ r, q] * factor + 2.0 * krond[q, s] * krond[r, p] * factor return dbe def d2q2element_ab(p, q, r, s, factor, tname_d1_1, tname_d1_2, tname_d2, tname_q2): if tname_d1_1 != 'ck_a': raise TypeError("For some reason I am expecting a ck_a. Ask Nick") dbe = DualBasisElement() dbe.add_element(tname_d1_1, (p, r), krond[q, s] * factor) dbe.add_element(tname_d1_2, (q, s), krond[p, r] * factor) dbe.add_element(tname_q2, (r, s, p, q), 1.0 * factor) dbe.add_element(tname_d2, (p, q, r, s), -1.0 * factor) dbe.dual_scalar = krond[q, s] * krond[p, r] * factor return dbe db = DualBasis() d2_names = ['cckk_aa', 'cckk_bb', 'cckk_ab'] q2_names = ['kkcc_aa', 'kkcc_bb', 'kkcc_ab'] d1_names_1 = ['ck_a', 'ck_b', 'ck_a'] d1_names_2 = ['ck_a', 'ck_b', 'ck_b'] dual_basis_list = [] for key in zip(d1_names_1, d1_names_2, d2_names, q2_names): d1_1, d1_2, d2_n, q2_n = key for p, q, r, s in product(range(dim), repeat=4): if (d2_n == 'cckk_aa' or d2_n == 'cckk_bb' ) and p < q and r < s and p * dim + q <= r * dim + s: dbe_1 = d2q2element(p, q, r, s, 0.5, d1_1, d1_2, d2_n, q2_n) dbe_2 = d2q2element(r, s, p, q, 0.5, d1_1, d1_2, d2_n, q2_n) # db += dbe_1.join_elements(dbe_2) dual_basis_list.append(dbe_1.join_elements(dbe_2)) elif d2_n == 'cckk_ab' and p * dim + q <= r * dim + s: dbe_1 = d2q2element_ab(p, q, r, s, 0.5, d1_1, d1_2, d2_n, q2_n) dbe_2 = d2q2element_ab(r, s, p, q, 0.5, d1_1, d1_2, d2_n, q2_n) # db += dbe_1.join_elements(dbe_2) dual_basis_list.append(dbe_1.join_elements(dbe_2)) return DualBasis(elements=dual_basis_list)
def test_d2_trace(): n_density, rdm_generator, transform, molecule = system() assert np.isclose(molecule.fci_energy, -2.84383506834) density = AntiSymmOrbitalDensity(n_density, molecule.n_qubits) tpdm_aa, tpdm_bb, tpdm_ab, _ = density.construct_tpdm() bas_aa, bas_ab = geminal_spin_basis(molecule.n_orbitals) tpdm_aa = Tensor(tpdm_aa, name='cckk_aa', basis=bas_aa) tpdm_bb = Tensor(tpdm_bb, name='cckk_bb', basis=bas_aa) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) rdms = MultiTensor([tpdm_aa, tpdm_bb, tpdm_ab]) dual_basis = trace_d2_aa(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, b, c = rdms.synthesize_dual_basis() Amat = A.todense() bmat = b.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) dual_basis = trace_d2_bb(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) dual_basis = trace_d2_ab(molecule.n_orbitals, molecule.n_electrons / 2, molecule.n_electrons / 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) db = DualBasis() db += trace_d2_aa(molecule.n_orbitals, molecule.n_electrons / 2) db += trace_d2_ab(molecule.n_orbitals, molecule.n_electrons / 2, molecule.n_electrons / 2) db += trace_d2_bb(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = db A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual))
def test_d2_trace_hubbard(): n_density, rdm_generator = system_hubbard() density = AntiSymmOrbitalDensity(n_density, 8) tpdm_aa, tpdm_bb, tpdm_ab, _ = density.construct_tpdm() bas_aa, bas_ab = geminal_spin_basis(4) tpdm_aa = Tensor(tpdm_aa, name='cckk_aa', basis=bas_aa) tpdm_bb = Tensor(tpdm_bb, name='cckk_bb', basis=bas_aa) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) rdms = MultiTensor([tpdm_aa, tpdm_bb, tpdm_ab]) dual_basis = trace_d2_aa(4, 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, b, c = rdms.synthesize_dual_basis() Amat = A.todense() bmat = b.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) dual_basis = trace_d2_bb(4, 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) dual_basis = trace_d2_ab(4, 2, 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) db = DualBasis() db += trace_d2_aa(4, 2) db += trace_d2_ab(4, 2, 2) db += trace_d2_bb(4, 2) rdms.dual_basis = db A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual))
def __init__(self, tensors, dual_basis=DualBasis()): """ A collection of tensor objects with algebraic maps from tensor to tensor In order to define a linear relationship between two tensors--i.e. the opdm and the oqdm-- a collection of maps objects can be generated. Mathematically, this is an object that allows you to define the dual basis on the vector space defined by the direct sum of all the tensors :param tensors: a dictionary or tuple of tensors and their associated call name :param DualBasisElement dual_basis: """ if not isinstance(tensors, list): raise TypeError("MultiTensor accepts a list") # this preserves the order the user passes with the tensors self.tensors = TMap(tensors) # since all the tensors are indexed from zero...I need to know their # numbering offset when combined with everything. self.off_set_map = self.make_offset_dict(self.tensors) # An iterable object that provides access to the dual basis elements self.dual_basis = dual_basis self.vec_dim = sum([vec.size for vec in self.tensors])
def antisymmetry_constraints(dim): """ The dual basis elements representing the antisymmetry constraints :param dim: spinless Fermion basis rank :return: the dual basis of antisymmetry_constraints :rtype: DualBasis """ # dual_basis = DualBasis() dbe_list = [] for p, q, r, s in product(range(dim), repeat=4): if p * dim + q <= r * dim + s: if p < q and r < s: tensor_elements = [ tuple(indices) for indices in _coord_generator(p, q, r, s) ] tensor_names = ['cckk'] * len(tensor_elements) tensor_coeffs = [0.5] * len(tensor_elements) dbe = DualBasisElement() for n, e, c in zip(tensor_names, tensor_elements, tensor_coeffs): dbe.add_element(n, e, c) # dual_basis += dbe dbe_list.append(dbe) return DualBasis(elements=dbe_list)
def test_d2_spin_sz_rep(): n_density, rdm_generator, transform, molecule = system() assert np.isclose(molecule.fci_energy, -2.84383506834) density = AntiSymmOrbitalDensity(n_density, molecule.n_qubits) tpdm_aa, tpdm_bb, tpdm_ab, _ = density.construct_tpdm() opdm_a, opdm_b = density.construct_opdm() bas_aa, bas_ab = geminal_spin_basis(molecule.n_orbitals) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) opdm_a = Tensor(opdm_a, name='ck_a') opdm_b = Tensor(opdm_b, name='ck_b') rdms = MultiTensor([opdm_a, opdm_b, tpdm_ab]) dim = int(np.sqrt(tpdm_ab.data.shape[0])) sz_rep_value = 0 for i in range(dim): sz_rep_value += 0.5 * (opdm_a.data[i, i] - opdm_b.data[i, i]) N = molecule.n_electrons M = 0 S = 0 db = DualBasis() db += s_representability_d2ab(dim, N, M, S) db += sz_representability(dim, M) rdms.dual_basis = db xvec = rdms.vectorize_tensors() A, _, b = rdms.synthesize_dual_basis() assert np.allclose(A.dot(xvec) - b, 0.0)
def test_d2_spin_rep_hubbard(): n_density, rdm_generator = system_hubbard() density = AntiSymmOrbitalDensity(n_density, 8) tpdm_aa, tpdm_bb, tpdm_ab, _ = density.construct_tpdm() bas_aa, bas_ab = geminal_spin_basis(4) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) rdms = MultiTensor([tpdm_ab]) dim = int(np.sqrt(tpdm_ab.data.shape[0])) s_rep_dual_constant = 0 for i, j in product(range(dim), repeat=2): s_rep_dual_constant += tpdm_ab.data[bas_ab.rev((i, j)), bas_ab.rev((j, i))] N = 4 M = 0 S = 0 db = DualBasis() db += s_representability_d2ab(dim, N, M, S) rdms.dual_basis = db xvec = rdms.vectorize_tensors() A, _, b = rdms.synthesize_dual_basis() assert np.allclose(A.dot(xvec) - b, 0.0) assert np.allclose(A.dot(xvec), s_rep_dual_constant)
def test_d2_spin_rep(): n_density, rdm_generator, transform, molecule = system() assert np.isclose(molecule.fci_energy, -2.84383506834) density = AntiSymmOrbitalDensity(n_density, molecule.n_qubits) tpdm_aa, tpdm_bb, tpdm_ab, _ = density.construct_tpdm() bas_aa, bas_ab = geminal_spin_basis(molecule.n_orbitals) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) rdms = MultiTensor([tpdm_ab]) dim = int(np.sqrt(tpdm_ab.data.shape[0])) s_rep_dual_constant = 0 for i, j in product(range(dim), repeat=2): s_rep_dual_constant += tpdm_ab.data[bas_ab.rev((i, j)), bas_ab.rev((j, i))] N = molecule.n_electrons M = 0 S = 0 db = DualBasis() db += s_representability_d2ab(dim, N, M, S) rdms.dual_basis = db xvec = rdms.vectorize_tensors() A, _, b = rdms.synthesize_dual_basis() assert np.allclose(A.dot(xvec) - b, 0.0) assert np.allclose(A.dot(xvec), s_rep_dual_constant)
def d2_to_t1_from_iterator(dim): """ Generate T1 from the iteratively generated dbe elements :param dim: :return: """ db = [] # NOTE: Figure out why join_elements is not working for p, q, r, i, j, k in product(range(dim), repeat=6): if p != q and q != r and p != r and i != j and j != k and i != k: print(p, q, r, i, j, k) dbe = DualBasisElement() dbe.dual_scalar -= t1_dual_scalar(p, q, r, i, j, k) for element in t1_opdm_component(p, q, r, i, j, k): # dbe.join_elements(element) # print(element.primal_tensors_names, element.primal_elements, element.primal_coeffs) dbe.add_element(element.primal_tensors_names[0], element.primal_elements[0], element.primal_coeffs[0]) for element in t1_tpdm_component(p, q, r, i, j, k): # dbe.join_elements(element) # print(element.primal_tensors_names, element.primal_elements, element.primal_coeffs) dbe.add_element(element.primal_tensors_names[0], element.primal_elements[0], element.primal_coeffs[0]) dbe.add_element('t1', (p, q, r, i, j, k), -1.0) db.append(dbe) return DualBasis(elements=db)
def d2_to_t1(dim): """ Generate the dual basis elements for mapping d2 and d1 to the T1-matrix The T1 condition is the sum of three-marginals T1 = <p^ q^ r^ i j k> i j k p^ q^ r^> in such a fashion that any triples component cancels out. T1 will be represented over antisymmeterized basis functions to save a significant amount of space :param dim: spin-orbital basis dimension :return: """ db = [] # DualBasis() for p, q, r, i, j, k in product(range(dim), repeat=6): # if (p * dim**2 + q * dim + r <= i * dim**2 + j * dim + k): print(p, q, r, i, j, k) dbe = DualBasisElement() dbe.dual_scalar -= ( (-1.0) * kdelta(i, p) * kdelta(j, q) * kdelta(k, r) + (1.0) * kdelta(i, p) * kdelta(j, r) * kdelta(k, q) + (1.0) * kdelta(i, q) * kdelta(j, p) * kdelta(k, r) + (-1.0) * kdelta(i, q) * kdelta(j, r) * kdelta(k, p) + (-1.0) * kdelta(i, r) * kdelta(j, p) * kdelta(k, q) + (1.0) * kdelta(i, r) * kdelta(j, q) * kdelta(k, p)) dbe.add_element('ck', (r, k), (1.0) * kdelta(i, p) * kdelta(j, q)) dbe.add_element('ck', (q, k), (-1.0) * kdelta(i, p) * kdelta(j, r)) dbe.add_element('ck', (r, j), (-1.0) * kdelta(i, p) * kdelta(k, q)) dbe.add_element('ck', (q, j), (1.0) * kdelta(i, p) * kdelta(k, r)) dbe.add_element('ck', (r, k), (-1.0) * kdelta(i, q) * kdelta(j, p)) dbe.add_element('ck', (p, k), (1.0) * kdelta(i, q) * kdelta(j, r)) dbe.add_element('ck', (r, j), (1.0) * kdelta(i, q) * kdelta(k, p)) dbe.add_element('ck', (p, j), (-1.0) * kdelta(i, q) * kdelta(k, r)) dbe.add_element('ck', (q, k), (1.0) * kdelta(i, r) * kdelta(j, p)) dbe.add_element('ck', (p, k), (-1.0) * kdelta(i, r) * kdelta(j, q)) dbe.add_element('ck', (q, j), (-1.0) * kdelta(i, r) * kdelta(k, p)) dbe.add_element('ck', (p, j), (1.0) * kdelta(i, r) * kdelta(k, q)) dbe.add_element('ck', (r, i), (1.0) * kdelta(j, p) * kdelta(k, q)) dbe.add_element('ck', (q, i), (-1.0) * kdelta(j, p) * kdelta(k, r)) dbe.add_element('ck', (r, i), (-1.0) * kdelta(j, q) * kdelta(k, p)) dbe.add_element('ck', (p, i), (1.0) * kdelta(j, q) * kdelta(k, r)) dbe.add_element('ck', (q, i), (1.0) * kdelta(j, r) * kdelta(k, p)) dbe.add_element('ck', (p, i), (-1.0) * kdelta(j, r) * kdelta(k, q)) dbe.add_element('cckk', (q, r, j, k), (1.0) * kdelta(i, p)) dbe.add_element('cckk', (p, r, j, k), (-1.0) * kdelta(i, q)) dbe.add_element('cckk', (p, q, j, k), (1.0) * kdelta(i, r)) dbe.add_element('cckk', (q, r, i, k), (-1.0) * kdelta(j, p)) dbe.add_element('cckk', (p, r, i, k), (1.0) * kdelta(j, q)) dbe.add_element('cckk', (p, q, i, k), (-1.0) * kdelta(j, r)) dbe.add_element('cckk', (q, r, i, j), (1.0) * kdelta(k, p)) dbe.add_element('cckk', (p, r, i, j), (-1.0) * kdelta(k, q)) dbe.add_element('cckk', (p, q, i, j), (1.0) * kdelta(k, r)) dbe.add_element('t1', (p, q, r, i, j, k), -1.0) dbe.simplify() db.append(dbe) return DualBasis(elements=db)
def sz_adapted_linear_constraints(dim, Na, Nb, constraint_list, S=0, M=0): """ Generate the dual basis for the v2-RDM program :param dim: rank of the spatial single-particle basis :param Na: Number of alpha electrons :param Nb: Number of beta electrons :param constraint_list: List of strings indicating which constraints to make :return: """ if Na != Nb and M == 0: raise TypeError("you gave me impossible quantum numbers") dual_basis = DualBasis() if 'cckk' in constraint_list: dual_basis += trace_d2_ab(dim, Na, Nb) dual_basis += s_representability_d2ab(dim, Na + Nb, M, S) # Including these would introduce linear independence. Why? dual_basis += trace_d2_aa(dim, Na) dual_basis += trace_d2_bb(dim, Nb) if Na == Nb: dual_basis += s_representability_d2ab_to_d2aa(dim) dual_basis += s_representability_d2ab_to_d2bb(dim) if 'ck' in constraint_list: if Na > 1: dual_basis += d2aa_d1a_mapping(dim, Na) dual_basis += trace_d2_aa(dim, Na) else: dual_basis += trace_d2_aa(dim, Na) if Nb > 1: dual_basis += d2bb_d1b_mapping(dim, Nb) dual_basis += trace_d2_bb(dim, Nb) else: dual_basis += trace_d2_bb(dim, Nb) dual_basis += d2ab_d1b_mapping(dim, Na) dual_basis += d2ab_d1a_mapping(dim, Nb) dual_basis += d1a_q1a_mapping(dim) dual_basis += d1b_q1b_mapping(dim) # dual_basis += d1a_d1b_mapping('ck_a', 'ck_b', dim) # this might not be needed if s_representability is enforced if Na + Nb > 2: dual_basis += sz_representability(dim, M) if 'kkcc' in constraint_list: dual_basis += d2_q2_mapping(dim) if 'ckck' in constraint_list: dual_basis += d2_g2_mapping(dim) return dual_basis
def nb_constraint(dim, nb): """ :param dim: :param sz: :return: """ dbe = DualBasisElement() for i in range(dim // 2): dbe.add_element('ck', (2 * i + 1, 2 * i + 1), 1.0) dbe.dual_scalar = nb return DualBasis(elements=[dbe])
def d2_g2_mapping(dim): """ Generate the mapping between d2 and g2 :param dim: linear dimension of the 1-RDM :return: the dual basis of the mapping :rtype: DualBasis """ krond = np.eye(dim) db = DualBasis() dbe_list = [] def g2d2map(p, q, r, s, factor=1): """ Build the dual basis element for a symmetric 2-marginal :param p: tensor index :param q: tensor index :param r: tensor index :param s: tensor index :param factor: weighting of the element :return: the dual basis element """ dbe = DualBasisElement() dbe.add_element('ck', (p, r), -1. * krond[q, s] * factor) dbe.add_element('ckck', (p, s, r, q), 1.0 * factor) dbe.add_element('cckk', (p, q, r, s), 1.0 * factor) dbe.dual_scalar = 0 return dbe for p, q, r, s in product(range(dim), repeat=4): if p * dim + q <= r * dim + s: db_element = g2d2map(p, q, r, s, factor=0.5) db_element_2 = g2d2map(r, s, p, q, factor=0.5) # db += db_element.join_elements(db_element_2) dbe_list.append(db_element.join_elements(db_element_2)) return DualBasis(elements=dbe_list) # db
def sz_constraint(dim, sz): """ Sz constraint is on the 1-RDM :param dim: :param sz: :return: """ dbe = DualBasisElement() for i in range(dim // 2): dbe.add_element('ck', (2 * i, 2 * i), 0.5) dbe.add_element('ck', (2 * i + 1, 2 * i + 1), -0.5) dbe.dual_scalar = sz return DualBasis(elements=[dbe])
def d2_e2_mapping(dim, measured_tpdm): """ Generate the constraints between the error matrix, d2, and a measured d2. :param dim: dimension of the spin-orbital basis :param measured_tpdm: a 4-tensor of the measured 2-p :return: """ db_elements = [] for p, q, r, s in product(range(dim), repeat=4): if p * dim + q >= r * dim + s: dbe = DualBasisElement() # two elements of d2 dbe.add_element('cckk', (p, q, r, s), 0.5) dbe.add_element('cckk', (r, s, p, q), 0.5) # add four elements of the error matrix dbe.add_element('cckk_me', (p * dim + q + dim**2, r * dim + s), -0.25) dbe.add_element('cckk_me', (r * dim + s + dim**2, p * dim + q), -0.25) dbe.add_element('cckk_me', (p * dim + q, r * dim + s + dim**2), -0.25) dbe.add_element('cckk_me', (r * dim + s, p * dim + q + dim**2), -0.25) dbe.dual_scalar = measured_tpdm[p, q, r, s].real dbe.simplify() # construct the dual basis element for constraining the [0, 0] orthant to be identity matrix dbe_idenity = DualBasisElement() if p * dim + q == r * dim + s: dbe_idenity.add_element('cckk_me', (p * dim + q, r * dim + s), 1.0) dbe_idenity.dual_scalar = 1.0 else: # will a symmetric constraint provide variational freedom? dbe_idenity.add_element('cckk_me', (p * dim + q, r * dim + s), 0.5) dbe_idenity.add_element('cckk_me', (r * dim + s, p * dim + q), 0.5) dbe_idenity.dual_scalar = 0.0 db_elements.append(dbe) db_elements.append(dbe_idenity) return DualBasis(elements=db_elements)
def _contraction_base(tname_d2, tname_d1, dim, normalization, offset): db = DualBasis() for i in range(dim): for j in range(i + offset, dim): dbe = DualBasisElement() for r in range(dim): dbe.add_element(tname_d2, (i, r, j, r), 0.5) dbe.add_element(tname_d2, (j, r, i, r), 0.5) dbe.add_element(tname_d1, (i, j), -0.5 * normalization) dbe.add_element(tname_d1, (j, i), -0.5 * normalization) dbe.dual_scalar = 0 # dbe.simplify() db += dbe return db
def d2bb_d1b_mapping(dim, Nb): """ Map the d2_spin-adapted 2-RDM to the D1 rdm :param Nb: number of beta electrons :param dim: :return: """ db = DualBasis() for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() for r in range(dim): # Not in the basis because always zero if i == r or j == r: continue else: sir = 1 if i < r else -1 sjr = 1 if j < r else -1 ir_pair = (i, r) if i < r else (r, i) jr_pair = (j, r) if j < r else (r, j) if i == j: dbe.add_element( 'cckk_bb', (ir_pair[0], ir_pair[1], jr_pair[0], jr_pair[1]), sir * sjr * 0.5) else: # TODO: Remember why I derived a factor of 0.25 (0.5 above) for this equation. dbe.add_element( 'cckk_bb', (ir_pair[0], ir_pair[1], jr_pair[0], jr_pair[1]), sir * sjr * 0.25) dbe.add_element( 'cckk_bb', (jr_pair[0], jr_pair[1], ir_pair[0], ir_pair[1]), sir * sjr * 0.25) dbe.add_element('ck_b', (i, j), -0.5 * (Nb - 1)) dbe.add_element('ck_b', (j, i), -0.5 * (Nb - 1)) dbe.dual_scalar = 0 dbe.simplify() db += dbe return db
def spin_orbital_linear_constraints(dim, N, constraint_list): """ Genrate the dual basis for the v2-RDM program :param dim: rank of spinless fermion basis :param N: Total number of electrons :param constraint_list: List of strings indicating which constraints to make :return: Dual basis for the constraint program :rtype: DualBasis """ dual_basis = DualBasis() if 'cckk' in constraint_list: print("d2 constraints") # trace constraint on D2 dual_basis += trace_constraint(dim, N * (N - 1)) # antisymmetry constraint print("antisymmetry constraint") dual_basis += antisymmetry_constraints(dim) if 'ck' in constraint_list: print("opdm constraints") dual_basis += d2_d1_mapping(dim, N - 1) dual_basis += d1_q1_mapping(dim) # d2 -> q2 if 'kkcc' in constraint_list: print('tqdm constraints') dual_basis += d2_q2_mapping(dim) # d2 -> g2 if "ckck" in constraint_list: print('phdm constraints') dual_basis += d2_g2_mapping(dim) # d2, d1 -> T1 if "t1" in constraint_list: # this uses an antisymmeterized form of the T1 matrix since it's really # not feasible to store r^{6} elements print('t1 constrinat') dual_basis += d2_to_t1_matrix_antisym(dim) return dual_basis
def d2aa_d1a_mapping(dim, Na): """ Map the d2_spin-adapted 2-RDM to the D1 rdm :param Nb: number of beta electrons :param dim: :return: """ db = DualBasis() for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() for r in range(dim): # Not in the basis because always zero if i == r or j == r: continue else: sir = 1 if i < r else -1 sjr = 1 if j < r else -1 ir_pair = (i, r) if i < r else (r, i) jr_pair = (j, r) if j < r else (r, j) if i == j: dbe.add_element( 'cckk_aa', (ir_pair[0], ir_pair[1], jr_pair[0], jr_pair[1]), sir * sjr) else: dbe.add_element( 'cckk_aa', (ir_pair[0], ir_pair[1], jr_pair[0], jr_pair[1]), sir * sjr * 0.5) dbe.add_element( 'cckk_aa', (jr_pair[0], jr_pair[1], ir_pair[0], ir_pair[1]), sir * sjr * 0.5) dbe.add_element('ck_a', (i, j), -0.5 * (Na - 1)) dbe.add_element('ck_a', (j, i), -0.5 * (Na - 1)) dbe.dual_scalar = 0 # dbe.simplify() db += dbe return db
def d2_q2_mapping(dim): """ Generate dual basis elements for d2-> q2 mapping :param dim: linear dimension of the 1-RDM :return: the dual basis of the mapping :rtype: DualBasis """ krond = np.eye(dim) # db = DualBasis() dbe_list = [] def d2q2element(p, q, r, s, factor): """ Build the dual basis element for symmetric form of 2-marginal :param p: tensor index :param q: tensor index :param r: tensor index :param s: tensor index :param factor: scaling coeff for a symmetric constraint :return: the dual basis of the mapping """ dbe = DualBasisElement() dbe.add_element('cckk', (p, q, r, s), -1.0 * factor) dbe.add_element('kkcc', (r, s, p, q), +1.0 * factor) dbe.add_element('ck', (p, r), krond[q, s] * factor) dbe.add_element('ck', (q, s), krond[p, r] * factor) dbe.add_element('ck', (p, s), -1. * krond[q, r] * factor) dbe.add_element('ck', (q, r), -1. * krond[p, s] * factor) dbe.dual_scalar = (krond[q, s] * krond[p, r] - krond[q, r] * krond[p, s]) * factor return dbe for p, q, r, s in product(range(dim), repeat=4): if p * dim + q <= r * dim + s: db_element = d2q2element(p, q, r, s, 0.5) db_element_2 = d2q2element(r, s, p, q, 0.5) # db += db_element.join_elements(db_element_2) dbe_list.append(db_element.join_elements(db_element_2)) return DualBasis(elements=dbe_list) # db
def _d1_q1_mapping(tname_d1, tname_q1, dim): db = DualBasis() for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() if i != j: dbe.add_element(tname_d1, (i, j), 0.5) dbe.add_element(tname_d1, (j, i), 0.5) dbe.add_element(tname_q1, (i, j), 0.5) dbe.add_element(tname_q1, (j, i), 0.5) dbe.dual_scalar = 0.0 else: dbe.add_element(tname_d1, (i, j), 1.0) dbe.add_element(tname_q1, (i, j), 1.0) dbe.dual_scalar = 1.0 db += dbe.simplify() return db
def d1a_d1b_mapping(tname_d1a, tname_d1b, dim): db = DualBasis() for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() if i != j: dbe.add_element(tname_d1a, (i, j), 0.5) dbe.add_element(tname_d1a, (j, i), 0.5) dbe.add_element(tname_d1b, (i, j), -0.5) dbe.add_element(tname_d1b, (j, i), -0.5) dbe.dual_scalar = 0.0 else: dbe.add_element(tname_d1a, (i, j), 1.0) dbe.add_element(tname_d1b, (i, j), -1.0) dbe.dual_scalar = 0.0 db += dbe # .simplify() return db
def d2ab_d1b_mapping(dim, Na): """ Map the d2_spin-adapted 2-RDM to the D1 rdm :param Nb: number of beta electrons :param dim: :return: """ db = DualBasis() for i in range(dim): for j in range(i, dim): dbe = DualBasisElement() for r in range(dim): dbe.add_element('cckk_ab', (r, i, r, j), 0.5) dbe.add_element('cckk_ab', (r, j, r, i), 0.5) dbe.add_element('ck_b', (i, j), -0.5 * Na) dbe.add_element('ck_b', (j, i), -0.5 * Na) dbe.dual_scalar = 0 # dbe.simplify() db += dbe return db
def test_d2_d1_mapping(): n_density, rdm_generator, transform, molecule = system_h4() density = AntiSymmOrbitalDensity(n_density, molecule.n_qubits) tpdm_aa, tpdm_bb, tpdm_ab, [bas_aa, bas_ab] = density.construct_tpdm() opdm_a, opdm_b = density.construct_opdm() from itertools import product test_opdm = np.zeros_like(opdm_a) for i, j in product(range(opdm_a.shape[0]), repeat=2): if i <= j: for r in range(opdm_a.shape[0]): if i != r and j != r: top_gem = tuple(sorted([i, r])) bot_gem = tuple(sorted([j, r])) parity = (-1)**(r < i) * (-1)**(r < j) if i == j: test_opdm[i, j] += tpdm_aa[bas_aa[top_gem], bas_aa[bot_gem]] * parity else: test_opdm[j, i] += tpdm_aa[bas_aa[top_gem], bas_aa[bot_gem]] * parity * 0.5 test_opdm[j, i] += tpdm_aa[bas_aa[bot_gem], bas_aa[top_gem]] * parity * 0.5 test_opdm[i, j] += tpdm_aa[bas_aa[top_gem], bas_aa[bot_gem]] * parity * 0.5 test_opdm[i, j] += tpdm_aa[bas_aa[bot_gem], bas_aa[top_gem]] * parity * 0.5 assert np.allclose(test_opdm, opdm_a) bas_aa, bas_ab = geminal_spin_basis(molecule.n_orbitals) opdm_a = Tensor(opdm_a, name='ck_a') opdm_b = Tensor(opdm_b, name='ck_b') tpdm_aa = Tensor(tpdm_aa, name='cckk_aa', basis=bas_aa) tpdm_bb = Tensor(tpdm_bb, name='cckk_bb', basis=bas_aa) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) rdms = MultiTensor([opdm_a, opdm_b, tpdm_aa, tpdm_bb, tpdm_ab]) # d2ab_d1a test dual_basis = DualBasis() dual_basis = d2ab_d1a_mapping(molecule.n_orbitals, molecule.n_electrons / 2) dual_basis += d2ab_d1b_mapping(molecule.n_orbitals, molecule.n_electrons / 2) dual_basis += d2aa_d1a_mapping(molecule.n_orbitals, molecule.n_electrons / 2) dual_basis += d2bb_d1b_mapping(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = dual_basis A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() w, v = np.linalg.eigh(np.dot(Amat, Amat.T)) cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual))
def d2_q2_mapping(dim): """ Map each d2 block to the q2 block :param dim: rank of spatial single-particle basis :return: """ krond = np.eye(dim) def d2q2element(p, q, r, s, factor, tname_d1_1, tname_d2, tname_q2): # , spin_string): """ # ( 1.00000) cre(r) cre(s) des(q) des(p) # ( 1.00000) kdelta(p,s) cre(r) des(q) # ( -1.00000) kdelta(p,r) cre(s) des(q) # ( -1.00000) kdelta(q,s) cre(r) des(p) # ( 1.00000) kdelta(q,r) cre(s) des(p) # ( -1.00000) kdelta(p,s) kdelta(q,r) # ( 1.00000) kdelta(p,r) kdelta(q,s) """ dbe = DualBasisElement() dbe.add_element(tname_q2, (p, q, r, s), -factor) dbe.add_element(tname_d2, (r, s, p, q), factor) if p == s: dbe.add_element(tname_d1_1, (r, q), factor) if p == r: dbe.add_element(tname_d1_1, (s, q), -factor) if q == s: dbe.add_element(tname_d1_1, (r, p), -factor) if q == r: dbe.add_element(tname_d1_1, (s, p), factor) # remember the negative sign because AX = b dbe.dual_scalar = -factor * (krond[p, r] * krond[q, s] - krond[p, s] * krond[q, r]) # dbe.add_element('kkcc_' + spin_string + spin_string, (r, s, p, q), factor) # if q == s: # dbe.add_element('ck_' + spin_string, (p, r), factor) # if p == s: # dbe.add_element('ck_' + spin_string, (q, r), -factor) # if q == r: # dbe.add_element('kc_' + spin_string, (s, p), factor) # if p == r: # dbe.add_element('kc_' + spin_string, (s, q), -factor) # dbe.add_element('cckk_' + spin_string + spin_string, (p, q, r, s), -factor) # dbe.dual_scalar = 0 return dbe def d2q2element_ab(p, q, r, s, factor): dbe = DualBasisElement() if q == s: dbe.add_element('ck_a', (p, r), factor) if p == r: dbe.add_element('kc_b', (s, q), -factor) dbe.add_element('kkcc_ab', (r, s, p, q), factor) dbe.add_element('cckk_ab', (p, q, r, s), -factor) # dbe.dual_scalar = -krond[q, s]*krond[p, r] * factor dbe.dual_scalar = 0 return dbe dual_basis_list = [] # for p, q, r, s in product(range(dim), repeat=4): from representability.tensor import index_tuple_basis gem_aa = [] gem_ab = [] for p, q in product(range(dim), repeat=2): if p < q: gem_aa.append((p, q)) gem_ab.append((p, q)) bas_aa = index_tuple_basis(gem_aa) bas_ab = index_tuple_basis(gem_ab) for i, j in product(range(dim * (dim - 1) // 2), repeat=2): if i >= j: p, q = bas_aa.fwd(i) r, s = bas_aa.fwd(j) # if p < q and r < s and p * dim + q < r * dim + s: dbe1 = d2q2element(p, q, r, s, 0.5, 'ck_a', 'cckk_aa', 'kkcc_aa') dbe2 = d2q2element(r, s, p, q, 0.5, 'ck_a', 'cckk_aa', 'kkcc_aa') # dbe1 = d2q2element(p, q, r, s, 0.5, 'a') # dbe2 = d2q2element(r, s, p, q, 0.5, 'a') dual_basis_list.append(dbe1.join_elements(dbe2)) # dbe1 = d2q2element(p, q, r, s, 0.5, 'b') # dbe2 = d2q2element(r, s, p, q, 0.5, 'b') dbe1 = d2q2element(p, q, r, s, 0.5, 'ck_b', 'cckk_bb', 'kkcc_bb') dbe2 = d2q2element(r, s, p, q, 0.5, 'ck_b', 'cckk_bb', 'kkcc_bb') dual_basis_list.append(dbe1.join_elements(dbe2)) # # if p < q and r < s and p == r and q == s: # # dbe1 = d2q2element(p, q, r, s, 1., 'a') # dbe = d2q2element(p, q, r, s, 1., 'ck_a', 'cckk_aa', 'kkcc_aa') # dual_basis_list.append(dbe) # # # dbe1 = d2q2element(p, q, r, s, 1., 'b') # dbe = d2q2element(p, q, r, s, 1., 'ck_b', 'cckk_bb', 'kkcc_bb') # dual_basis_list.append(dbe) for i, j in product(range(dim * dim), repeat=2): if i >= j: p, q = bas_ab.fwd(i) r, s = bas_ab.fwd(j) # if p * dim + q <= r * dim + s: dbe1 = d2q2element_ab(p, q, r, s, 0.5) dbe2 = d2q2element_ab(r, s, p, q, 0.5) dual_basis_list.append(dbe1.join_elements(dbe2)) return DualBasis(elements=dual_basis_list)
def d2_e2_mapping(dim, bas_aa, bas_ab, measured_tpdm_aa, measured_tpdm_bb, measured_tpdm_ab): """ Generate constraints such that the error matrix and the d2 matrices look like the measured matrices :param dim: spatial basis dimension :param measured_tpdm_aa: two-marginal of alpha-alpha spins :param measured_tpdm_bb: two-marginal of beta-beta spins :param measured_tpdm_ab: two-marginal of alpha-beta spins :return: """ db = DualBasis() # first constrain the aa-matrix aa_dim = dim * (dim - 1) / 2 ab_dim = dim**2 # map the aa matrix to the measured_tpdm_aa for p, q, r, s in product(range(dim), repeat=4): if p < q and r < s and bas_aa[(p, q)] <= bas_aa[(r, s)]: dbe = DualBasisElement() # two elements of D2aa dbe.add_element('cckk_aa', (p, q, r, s), 0.5) dbe.add_element('cckk_aa', (r, s, p, q), 0.5) # four elements of the E2aa dbe.add_element('cckk_me_aa', (bas_aa[(p, q)] + aa_dim, bas_aa[(r, s)]), 0.25) dbe.add_element('cckk_me_aa', (bas_aa[(r, s)] + aa_dim, bas_aa[(p, q)]), 0.25) dbe.add_element('cckk_me_aa', (bas_aa[(p, q)], bas_aa[(r, s)] + aa_dim), 0.25) dbe.add_element('cckk_me_aa', (bas_aa[(r, s)], bas_aa[(p, q)] + aa_dim), 0.25) dbe.dual_scalar = measured_tpdm_aa[bas_aa[(p, q)], bas_aa[(r, s)]].real dbe.simplify() # construct the dbe for constraining the [0, 0] orthant to the idenity matrix dbe_identity_aa = DualBasisElement() if bas_aa[(p, q)] == bas_aa[(r, s)]: dbe_identity_aa.add_element('cckk_me_aa', (bas_aa[(p, q)], bas_aa[(r, s)]), 1.0) dbe_identity_aa.dual_scalar = 1.0 else: dbe_identity_aa.add_element('cckk_me_aa', (bas_aa[(p, q)], bas_aa[(r, s)]), 0.5) dbe_identity_aa.add_element('cckk_me_aa', (bas_aa[(r, s)], bas_aa[(p, q)]), 0.5) dbe_identity_aa.dual_scalar = 0.0 db += dbe db += dbe_identity_aa # map the bb matrix to the measured_tpdm_bb for p, q, r, s in product(range(dim), repeat=4): if p < q and r < s and bas_aa[(p, q)] <= bas_aa[(r, s)]: dbe = DualBasisElement() # two elements of D2bb dbe.add_element('cckk_bb', (p, q, r, s), 0.5) dbe.add_element('cckk_bb', (r, s, p, q), 0.5) # four elements of the E2bb dbe.add_element('cckk_me_bb', (bas_aa[(p, q)] + aa_dim, bas_aa[(r, s)]), 0.25) dbe.add_element('cckk_me_bb', (bas_aa[(r, s)] + aa_dim, bas_aa[(p, q)]), 0.25) dbe.add_element('cckk_me_bb', (bas_aa[(p, q)], bas_aa[(r, s)] + aa_dim), 0.25) dbe.add_element('cckk_me_bb', (bas_aa[(r, s)], bas_aa[(p, q)] + aa_dim), 0.25) dbe.dual_scalar = measured_tpdm_bb[bas_aa[(p, q)], bas_aa[(r, s)]].real dbe.simplify() # construct the dbe for constraining the [0, 0] orthant to the idenity matrix dbe_identity_bb = DualBasisElement() if bas_aa[(p, q)] == bas_aa[(r, s)]: dbe_identity_bb.add_element('cckk_me_bb', (bas_aa[(p, q)], bas_aa[(r, s)]), 1.0) dbe_identity_bb.dual_scalar = 1.0 else: dbe_identity_bb.add_element('cckk_me_bb', (bas_aa[(p, q)], bas_aa[(r, s)]), 0.5) dbe_identity_bb.add_element('cckk_me_bb', (bas_aa[(r, s)], bas_aa[(p, q)]), 0.5) dbe_identity_bb.dual_scalar = 0.0 db += dbe db += dbe_identity_bb # map the ab matrix to the measured_tpdm_ab for p, q, r, s in product(range(dim), repeat=4): if bas_ab[(p, q)] <= bas_ab[(r, s)]: dbe = DualBasisElement() # two elements of D2ab dbe.add_element('cckk_ab', (p, q, r, s), 0.5) dbe.add_element('cckk_ab', (r, s, p, q), 0.5) # four elements of the E2ab dbe.add_element('cckk_me_ab', (bas_ab[(p, q)] + ab_dim, bas_ab[(r, s)]), 0.25) dbe.add_element('cckk_me_ab', (bas_ab[(r, s)] + ab_dim, bas_ab[(p, q)]), 0.25) dbe.add_element('cckk_me_ab', (bas_ab[(p, q)], bas_ab[(r, s)] + ab_dim), 0.25) dbe.add_element('cckk_me_ab', (bas_ab[(r, s)], bas_ab[(p, q)] + ab_dim), 0.25) dbe.dual_scalar = measured_tpdm_ab[bas_ab[(p, q)], bas_ab[(r, s)]].real dbe.simplify() # construct the dbe for constraining the [0, 0] orthant to the idenity matrix dbe_identity_ab = DualBasisElement() if bas_ab[(p, q)] == bas_ab[(r, s)]: dbe_identity_ab.add_element('cckk_me_ab', (bas_ab[(p, q)], bas_ab[(r, s)]), 1.0) dbe_identity_ab.dual_scalar = 1.0 else: dbe_identity_ab.add_element('cckk_me_ab', (bas_ab[(p, q)], bas_ab[(r, s)]), 0.5) dbe_identity_ab.add_element('cckk_me_ab', (bas_ab[(r, s)], bas_ab[(p, q)]), 0.5) dbe_identity_ab.dual_scalar = 0.0 db += dbe db += dbe_identity_ab return db
def d2_g2_mapping(dim): """ Map each d2 blcok to the g2 blocks :param dim: rank of spatial single-particle basis :return: """ krond = np.eye(dim) # d2 -> g2 def g2d2map_aa_or_bb(p, q, r, s, dim, key, factor=1.0): """ Accept pqrs of G2 and map to D2 """ dbe = DualBasisElement() quad = {'aa': [0, 0], 'bb': [1, 1]} dbe.add_element('ckck_aabb', (p * dim + q + quad[key][0] * dim**2, r * dim + s + quad[key][1] * dim**2), -1.0 * factor) if q == s: dbe.add_element('ck_' + key[0], (p, r), krond[q, s] * factor) if p != s and r != q: gem1 = tuple(sorted([p, s])) gem2 = tuple(sorted([r, q])) parity = (-1)**(p < s) * (-1)**(r < q) # factor of 0.5 is from the spin-adapting dbe.add_element('cckk_' + key, (gem1[0], gem1[1], gem2[0], gem2[1]), parity * -factor) dbe.dual_scalar = 0 return dbe def g2d2map_aabb(p, q, r, s, dim, key, factor=1.0): """ Accept pqrs of G2 and map to D2 """ dbe = DualBasisElement() # this is ugly. :( quad = {'aabb': [0, 1], 'bbaa': [1, 0]} dbe.add_element('ckck_aabb', (p * dim + q + quad[key][0] * dim**2, r * dim + s + quad[key][1] * dim**2), 1.0 * factor) dbe.add_element('ckck_aabb', (r * dim + s + quad[key[::-1]][0] * dim**2, p * dim + q + quad[key[::-1]][1] * dim**2), 1.0 * factor) dbe.add_element('cckk_ab', (p, s, q, r), -1.0 * factor) dbe.add_element('cckk_ab', (q, r, p, s), -1.0 * factor) dbe.dual_scalar = 0.0 return dbe def g2d2map_ab(p, q, r, s, key, factor=1.0): dbe = DualBasisElement() if key == 'ab': if q == s: dbe.add_element('ck_' + key[0], (p, r), krond[q, s] * factor) dbe.add_element('cckk_' + key, (p, s, r, q), -1.0 * factor) elif key == 'ba': if q == s: dbe.add_element('ck_' + key[0], (p, r), krond[q, s] * factor) dbe.add_element('cckk_ab', (s, p, q, r), -1.0 * factor) else: raise TypeError("I only accept ab or ba blocks") dbe.add_element('ckck_' + key, (p, q, r, s), -1.0 * factor) dbe.dual_scalar = 0.0 return dbe dual_basis_list = [] # do aa_aa block then bb_block of the superblock for key in ['bb', 'aa']: for p, q, r, s in product(range(dim), repeat=4): if p * dim + q <= r * dim + s: dbe_1 = g2d2map_aa_or_bb(p, q, r, s, dim, key, factor=0.5) dbe_2 = g2d2map_aa_or_bb(r, s, p, q, dim, key, factor=0.5) dual_basis_list.append(dbe_1.join_elements(dbe_2)) # this constraint is over the entire block! for key in ['aabb']: for p, q, r, s in product(range(dim), repeat=4): dbe = g2d2map_aabb(p, q, r, s, dim, key, factor=1.0) # db += dbe dual_basis_list.append(dbe) # # ab ba blocks of G2 for key in ['ab', 'ba']: for p, q, r, s in product(range(dim), repeat=4): if p * dim + q <= r * dim + s: dbe_1 = g2d2map_ab(p, q, r, s, key, factor=0.5) dbe_2 = g2d2map_ab(r, s, p, q, key, factor=0.5) dual_basis_list.append(dbe_1.join_elements(dbe_2)) return DualBasis(elements=dual_basis_list)
def test_d2_trace_h4(): n_density, rdm_generator, transform, molecule = system_h4() density = AntiSymmOrbitalDensity(n_density, molecule.n_qubits) dim = molecule.n_orbitals tpdm_aa, tpdm_bb, tpdm_ab, _ = density.construct_tpdm() Na, Nb = 2, 2 trace_ab = 0 for i, j in product(range(molecule.n_orbitals), repeat=2): trace_ab += tpdm_ab[i * dim + j, i * dim + j] assert np.isclose(trace_ab, Na, Nb) assert np.isclose(np.trace(tpdm_aa), Na * (Na - 1) / 2) assert np.isclose(np.trace(tpdm_bb), Nb * (Nb - 1) / 2) bas_aa, bas_ab = geminal_spin_basis(molecule.n_orbitals) tpdm_aa = Tensor(tpdm_aa, name='cckk_aa', basis=bas_aa) tpdm_bb = Tensor(tpdm_bb, name='cckk_bb', basis=bas_aa) tpdm_ab = Tensor(tpdm_ab, name='cckk_ab', basis=bas_ab) rdms = MultiTensor([tpdm_aa, tpdm_bb, tpdm_ab]) dual_basis = trace_d2_aa(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, b, c = rdms.synthesize_dual_basis() Amat = A.todense() bmat = b.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) dual_basis = trace_d2_bb(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) dual_basis = trace_d2_ab(molecule.n_orbitals, molecule.n_electrons / 2, molecule.n_electrons / 2) rdms.dual_basis = DualBasis(elements=[dual_basis]) A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual)) db = DualBasis() db += trace_d2_aa(molecule.n_orbitals, molecule.n_electrons / 2) db += trace_d2_ab(molecule.n_orbitals, molecule.n_electrons / 2, molecule.n_electrons / 2) db += trace_d2_bb(molecule.n_orbitals, molecule.n_electrons / 2) rdms.dual_basis = db A, _, c = rdms.synthesize_dual_basis() Amat = A.todense() cmat = c.todense() primal_vec = rdms.vectorize_tensors() residual = Amat.dot(primal_vec) - cmat assert np.allclose(residual, np.zeros_like(residual))