def test_antisymmeterizer(): dim = 10 np.random.seed(42) mat = np.random.random((dim, dim, dim, dim)) sym_mat = symmeterize_four_tensor(mat) mat_anti = antisymmeterizer(sym_mat) check_antisymmetric_d2(mat_anti)
def test_unitary_subspace_purification_fixed_opdm(): n_density, rdm_gen, transform, molecule = heh_system() density = SpinOrbitalDensity(n_density, molecule.n_qubits) tpdm = density.construct_tpdm() tpdm_noise = add_gaussian_noise_antisymmetric_four_tensor(tpdm, 0.00001) check_antisymmetric_d2(tpdm_noise.real) tpdm_projected = unitary_subspace_purification_fixed_initial_trace( tpdm_noise.real, molecule.n_electrons, disp=True) d2_matrix = four_tensor2matrix(tpdm_projected) w, v = np.linalg.eigh(d2_matrix) assert all(w > -1.0E-6)
def test_coleman_decomp(): n_density, rdm_gen, transform, molecule = heh_system() density = SpinOrbitalDensity(n_density, molecule.n_qubits) tpdm = density.construct_tpdm() opdm = density.construct_opdm() dim = tpdm.shape[0] # check if we get a valid tpdm check_antisymmetric_d2(tpdm) # now test the Coleman-Absar decomposition d2_0, d2_1, d2_2 = coleman_decomposition(tpdm) # now check some properties. # 1. check d2_2 contracts to zero d1_zero = np.einsum('ikjk', d2_2) assert np.allclose(d1_zero, 0.0) # 2. check orthogonality of tenors assert np.isclose(np.einsum('ijkl, lkji', d2_0, d2_1), 0.0) assert np.isclose(np.einsum('ijkl, lkji', d2_1, d2_2), 0.0) assert np.isclose(np.einsum('ijkl, lkji', d2_2, d2_0), 0.0) # 3. check reconstruct to tpdm assert np.allclose(d2_0 + d2_1 + d2_2, tpdm) # 4. check D1 looks like a contracted d2 up to coefficients one_one_tensor = np.einsum('ikjk', tpdm) N = molecule.n_electrons # why are we missing the factor of 2? Is it because of normalization on D2? assert np.allclose(one_one_tensor, (N - 1) * opdm) # 5. Check trace of d2_0 is same as tpdm assert np.isclose(np.einsum('ijij', tpdm), np.einsum('ijij', d2_0)) # 6. Check one-trace (d2_0 + d2_1) = a1 assert np.allclose(np.einsum('ikjk', d2_0 + d2_1), one_one_tensor)
def unitary_subspace_purification_fixed_initial_trace(tpdm, N, error=1.0E-6, disp=False): """ Fixe up the tpdm trace and 1-particle piece and then iterate over the Coleman decomp of exposed operators :param tpdm: :param N: :param float error: :param Bool disp: optional argument for displaying updates :return: """ if __debug__: check_antisymmetric_d2(tpdm) dim = tpdm.shape[0] _, _, d2_2 = coleman_decomposition(tpdm) # check if this thing I grabbed is actually an antisymmetric operator if __debug__: check_antisymmetric_d2(d2_2) assert np.isclose(np.einsum('ijij', d2_2), 0.0) eye_wedge_eye = wedge_product(np.eye(dim), np.eye(dim)).astype(complex) # we know d2_0 by fixed trace from particle number trace_value = N * (N - 1) d2_0 = ((2 * trace_value) / (dim * (dim - 1))) * eye_wedge_eye # we know d2_1 should be (N - 1) * opdm # so grab the opdm first from the noisy tpdm and then purify opdm = map_tpdm_to_opdm(tpdm, N) # opdm_new = mazziotti_opdm_purification(opdm, N) opdm_new = fixed_trace_positive_projection(opdm, N) # first multiply by N - 1 get back to just contracted. Then another # for adjusting one_one_tensor = ((N - 1)**2) * opdm_new one_one_eye = wedge_product(one_one_tensor, np.eye(dim)) d2_1 = (4 / (dim - 2)) * one_one_eye d2_1 -= ((4 * trace_value) / (dim * (dim - 2))) * eye_wedge_eye if __debug__: d2_1_trial = (4 / (dim - 2)) * wedge_product( opdm_new - (trace_value / dim) * np.eye(dim), np.eye(dim)) assert np.allclose(d2_1_trial, d2_1) # we need to check a bunch of stuff about the reconstruct tpdm # a) does it have all the right symmetry # b) does it have the right trace and does it contract appropriately # c) does the d2_0 + d2_1 look like a contracted tpdm_partial fix tpdm_partial_fix = d2_0 + d2_1 + d2_2 d2_matrix = four_tensor2matrix(tpdm_partial_fix) if __debug__: check_antisymmetric_d2(tpdm_partial_fix) # check conversion back to the tpdm_partial_fix np.testing.assert_allclose(matrix2four_tensor(d2_matrix), tpdm_partial_fix) residual = 10 iter_max = 3000 iter = 0 while residual > error and iter < iter_max: w, v = np.linalg.eigh(d2_matrix) exposed_operator = [] for ii in range(w.shape[0]): if w[ii] < float(-1.0E-13): if __debug__: # checks to see if I'm sane? assert v[:, [ii]].shape == (d2_matrix.shape[0], 1) assert v[:, [ii]].dot(np.conj( v[:, [ii]]).T).shape == d2_matrix.shape # check_antisymmetric_index(v[:, [ii]]) # get the exposing operator antisymmeterize eo = v[:, [ii]].dot(np.conj(v[:, [ii]]).T) eo_22_tensor = matrix2four_tensor(eo) eo_22_tensor_antiy = antisymmeterizer(eo_22_tensor) if __debug__: check_antisymmetric_d2( eo_22_tensor_antiy) # redundant sanity check # check if operator is hermetian np.testing.assert_allclose(eo, np.conj(eo).T) # add the operator to my list exposed_operator.append(four_tensor2matrix(eo_22_tensor_antiy)) # Now we want to find the coleman decomp of each of the exposing operators num_eo = len(exposed_operator) zero_contraction_eo = [] for ii in range(num_eo): eo = matrix2four_tensor(exposed_operator[ii]) eo_0, eo_1, eo_2 = coleman_decomposition(eo) zero_contraction_eo.append(four_tensor2matrix(eo_2)) if __debug__: # check result is sane assert np.isclose(np.einsum('ijij', eo_0), np.einsum('ijij', eo)) # check if it contracts to zero assert np.allclose(np.einsum('ikjk', eo_2), np.zeros_like(eo_2)) # check if the einsum is correct assert np.allclose(np.einsum('ikjk', eo), np.einsum('ikjk', eo_0 + eo_1)) # check if it reconstructs assert np.allclose(eo_0 + eo_1 + eo_2, eo) # check if it's hermetian assert np.allclose(zero_contraction_eo[-1], np.conj(zero_contraction_eo[-1]).T) # check if eo_2 is antisymmetric check_antisymmetric_d2(eo_2) # set up system of equations to solve for alpha terms Amatrix = np.zeros((num_eo, num_eo)) bvector = np.zeros((num_eo, 1)) for i, j in product(range(num_eo), repeat=2): Amatrix[i, j] = np.trace(exposed_operator[i].dot( zero_contraction_eo[j])).real if i == j: bvector[i, 0] = np.trace(d2_matrix.dot(exposed_operator[i])).real # alpha = np.linalg.solve(Amatrix, -bvector) [alpha, _, _, _] = np.linalg.lstsq(Amatrix, -bvector) if __debug__: assert np.allclose(np.dot(Amatrix, alpha), -bvector) # update the 2-RDM matrix d2_new = d2_matrix.copy() for ii in range(num_eo): d2_new += alpha[ii] * zero_contraction_eo[ii] if __debug__: # check if the new d2_new is orthogonal to zero_contraction for ii in range(num_eo): assert np.isclose(np.trace(d2_new.dot(exposed_operator[ii])), 0.0) # check for conversion of the iterative method w, v = np.linalg.eigh(d2_new) residual = np.linalg.norm(w[w < 0]) if disp: print("iter {:5.0f}\tdiff {:3.10e}\t".format( iter, np.linalg.norm(d2_new - d2_matrix)), end='') print("trace new {:3.10f}\terror {:3.10e}".format( np.trace(d2_new).real, residual)) d2_matrix = d2_new iter += 1 tpdm = matrix2four_tensor(d2_matrix) return tpdm