def test_apply_spinful_fermionop(self):
        """
        Make sure the spin-orbital reordering is working by comparing
        apply operation
        """
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')
        wfn.normalize()
        cirq_wf = to_cirq(wfn).reshape((-1, 1))

        op_to_apply = FermionOperator()
        test_state = copy.deepcopy(wfn)
        test_state.set_wfn('zero')
        for p, q, r, s in product(range(2), repeat=4):
            op = FermionOperator(
                ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)),
                coefficient=numpy.random.randn())
            op_to_apply += op + hermitian_conjugated(op)
            test_state += wfn.apply(op + hermitian_conjugated(op))

        opmat = get_sparse_operator(op_to_apply, n_qubits=4).toarray()
        new_state_cirq = opmat @ cirq_wf

        # this part is because we need to pass a normalized wavefunction
        norm_constant = new_state_cirq.conj().T @ new_state_cirq
        new_state_cirq /= numpy.sqrt(norm_constant)
        new_state_wfn = from_cirq(new_state_cirq.flatten(), thresh=1.0E-12)
        new_state_wfn.scale(numpy.sqrt(norm_constant))

        self.assertTrue(
            numpy.allclose(test_state.get_coeff((2, 0)),
                           new_state_wfn.get_coeff((2, 0))))
    def test_evolve_spinful_fermionop(self):
        """
        Make sure the spin-orbital reordering is working by comparing
        time evolution
        """
        wfn = Wavefunction([[2, 0, 2]])
        wfn.set_wfn(strategy='random')
        wfn.normalize()
        cirq_wf = to_cirq(wfn).reshape((-1, 1))

        op_to_apply = FermionOperator()
        for p, q, r, s in product(range(2), repeat=4):
            op = FermionOperator(
                ((2 * p, 1), (2 * q + 1, 1), (2 * r + 1, 0), (2 * s, 0)),
                coefficient=numpy.random.randn())
            op_to_apply += op + hermitian_conjugated(op)

        opmat = get_sparse_operator(op_to_apply, n_qubits=4).toarray()
        dt = 0.765
        new_state_cirq = scipy.linalg.expm(-1j * dt * opmat) @ cirq_wf
        new_state_wfn = from_cirq(new_state_cirq.flatten(), thresh=1.0E-12)
        test_state = wfn.time_evolve(dt, op_to_apply)
        self.assertTrue(
            numpy.allclose(test_state.get_coeff((2, 0)),
                           new_state_wfn.get_coeff((2, 0))))
 def fermion_generator(self) -> openfermion.FermionOperator:
     """The FermionOperator G such that the gate's unitary is exp(-i t G)
     with exponent t using the Jordan-Wigner transformation."""
     half_generator = sum((
         w * G
         for w, G in zip(self.weights, self.fermion_generator_components())),
                          openfermion.FermionOperator())
     return half_generator + openfermion.hermitian_conjugated(half_generator)
Esempio n. 4
0
    def operations(self, qubits: Sequence[cirq.Qid]) -> cirq.OP_TREE:
        """Returns qubit operators based on STO-3G and UCCSD ansatz"""
        _symbols = list(self.params())
        _ucc_operator = self._fermionic_operators  # self._get_fermionic_operators()

        for i, fo in enumerate(_ucc_operator[::2]):
            # Jordan-Wigner transform each fermionic operator + its Hermitian conjugate
            qo_list = list(of.jordan_wigner(fo - of.hermitian_conjugated(fo)))
            yield IGeneralizedUCCSD.qubit_to_gate_operators(
                qubit_operators=qo_list, qubits=qubits, par=_symbols[i])
Esempio n. 5
0
    def test_qubitop_to_dict_io(self):
        # Given
        qubit_op = QubitOperator(((0, 'Y'), (1, 'X'), (2, 'Z'), (4, 'X')), 3.j)
        qubit_op += hermitian_conjugated(qubit_op)

        # When
        qubitop_dict = convert_qubitop_to_dict(qubit_op)
        recreated_qubit_op = convert_dict_to_qubitop(qubitop_dict)

        # Then
        self.assertEqual(recreated_qubit_op, qubit_op)
Esempio n. 6
0
def test_double_excitation_matches_fermionic_evolution(exponent):
    gate = ofc.DoubleExcitation ** exponent

    op = openfermion.FermionOperator('3^ 2^ 1 0')
    op += openfermion.hermitian_conjugated(op)
    matrix_op = openfermion.get_sparse_operator(op)

    time_evol_op = scipy.linalg.expm(-1j * matrix_op * exponent * numpy.pi)
    time_evol_op = time_evol_op.todense()

    cirq.testing.assert_allclose_up_to_global_phase(
        cirq.unitary(gate), time_evol_op, atol=1e-7)
def test_double_excitation_matches_fermionic_evolution(half_turns):
    gate = DoubleExcitation ** half_turns

    op = openfermion.FermionOperator('3^ 2^ 1 0')
    op += openfermion.hermitian_conjugated(op)
    matrix_op = openfermion.get_sparse_operator(op)

    time_evol_op = scipy.linalg.expm(-1j * matrix_op * half_turns * numpy.pi)
    time_evol_op = time_evol_op.todense()

    cirq.testing.assert_allclose_up_to_global_phase(
        gate.matrix(), time_evol_op, atol=1e-7)
Esempio n. 8
0
    def test_interaction_op_to_dict_io(self):
        # Given
        test_op = FermionOperator("1^ 2^ 3 4")
        test_op += hermitian_conjugated(test_op)
        interaction_op = get_interaction_operator(test_op)
        interaction_op.constant = 0.0

        # When
        interaction_op_dict = convert_interaction_op_to_dict(interaction_op)
        recreated_interaction_op = convert_dict_to_interaction_op(
            interaction_op_dict)

        # Then
        self.assertEqual(recreated_interaction_op, interaction_op)
Esempio n. 9
0
    def test_interaction_operator_io(self):
        # Given
        test_op = FermionOperator("1^ 2^ 3 4")
        test_op += hermitian_conjugated(test_op)
        interaction_op = get_interaction_operator(test_op)
        interaction_op.constant = 0.0

        # When
        save_interaction_operator(interaction_op, "interaction_op.json")
        loaded_op = load_interaction_operator("interaction_op.json")

        # Then
        self.assertEqual(interaction_op, loaded_op)
        os.remove("interaction_op.json")
Esempio n. 10
0
def test_generalized_doubles():
    generator = generate_antisymm_generator(2)
    nso = generator.shape[0]
    for p, q, r, s in product(range(nso), repeat=4):
        if p < q and s < r:
            assert np.isclose(generator[p, q, r, s], -generator[q, p, r, s])

    ul, vl, one_body_residual, ul_ops, vl_ops, one_body_op = \
        doubles_factorization_svd(generator)

    generator_mat = np.reshape(np.transpose(generator, [0, 3, 1, 2]),
                               (nso**2, nso**2)).astype(np.float)
    one_body_residual_test = -np.einsum('pqrq->pr', generator)
    assert np.allclose(generator_mat, generator_mat.T)
    assert np.allclose(one_body_residual, one_body_residual_test)

    tgenerator_mat = np.zeros_like(generator_mat)
    for row_gem, col_gem in product(range(nso**2), repeat=2):
        p, s = row_gem // nso, row_gem % nso
        q, r = col_gem // nso, col_gem % nso
        tgenerator_mat[row_gem, col_gem] = generator[p, q, r, s]

    assert np.allclose(tgenerator_mat, generator_mat)

    u, sigma, vh = np.linalg.svd(generator_mat)

    fop = copy.deepcopy(one_body_op)
    fop2 = copy.deepcopy(one_body_op)
    fop3 = copy.deepcopy(one_body_op)
    fop4 = copy.deepcopy(one_body_op)
    for ll in range(len(sigma)):
        ul.append(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso)))
        ul_ops.append(
            get_fermion_op(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso))))
        vl.append(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso)))
        vl_ops.append(
            get_fermion_op(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso))))
        Smat = ul[ll] + vl[ll]
        Dmat = ul[ll] - vl[ll]

        S = ul_ops[ll] + vl_ops[ll]
        Sd = of.hermitian_conjugated(S)
        D = ul_ops[ll] - vl_ops[ll]
        Dd = of.hermitian_conjugated(D)
        op1 = S + 1j * of.hermitian_conjugated(S)
        op2 = S - 1j * of.hermitian_conjugated(S)
        op3 = D + 1j * of.hermitian_conjugated(D)
        op4 = D - 1j * of.hermitian_conjugated(D)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op1, of.hermitian_conjugated(op1))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op2, of.hermitian_conjugated(op2))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op3, of.hermitian_conjugated(op3))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op4, of.hermitian_conjugated(op4))).induced_norm(), 0)

        fop3 += (1 / 8) * ((S**2 - Sd**2) - (D**2 - Dd**2))
        fop4 += (1 / 16) * ((op1**2 + op2**2) - (op3**2 + op4**2))

        op1mat = Smat + 1j * Smat.T
        op2mat = Smat - 1j * Smat.T
        op3mat = Dmat + 1j * Dmat.T
        op4mat = Dmat - 1j * Dmat.T

        assert np.allclose(of.commutator(op1mat, op1mat.conj().T), 0)
        assert np.allclose(of.commutator(op2mat, op2mat.conj().T), 0)
        assert np.allclose(of.commutator(op3mat, op3mat.conj().T), 0)
        assert np.allclose(of.commutator(op4mat, op4mat.conj().T), 0)

        # check that we have normal operators and that the outer product
        # of their eigenvalues is imaginary. Also check vv is unitary
        if not np.isclose(sigma[ll], 0):
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op1mat) - op1).induced_norm(),
                0)
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op2mat) - op2).induced_norm(),
                0)
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op3mat) - op3).induced_norm(),
                0)
            assert np.isclose(
                of.normal_ordered(get_fermion_op(op4mat) - op4).induced_norm(),
                0)

            ww, vv = np.linalg.eig(op1mat)
            eye = np.eye(nso)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)
            ww, vv = np.linalg.eig(op2mat)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)
            ww, vv = np.linalg.eig(op3mat)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)
            ww, vv = np.linalg.eig(op4mat)
            assert np.allclose(np.outer(ww, ww).real, 0)
            assert np.allclose(vv.conj().T @ vv, eye)

        fop2 += 0.25 * ul_ops[ll] * vl_ops[ll]
        fop2 += 0.25 * vl_ops[ll] * ul_ops[ll]
        fop2 += -0.25 * of.hermitian_conjugated(
            vl_ops[ll]) * of.hermitian_conjugated(ul_ops[ll])
        fop2 += -0.25 * of.hermitian_conjugated(
            ul_ops[ll]) * of.hermitian_conjugated(vl_ops[ll])

        fop += vl_ops[ll] * ul_ops[ll]

    true_fop = get_fermion_op(generator)
    assert np.isclose(of.normal_ordered(fop - true_fop).induced_norm(), 0)
    assert np.isclose(of.normal_ordered(fop2 - true_fop).induced_norm(), 0)
    assert np.isclose(of.normal_ordered(fop3 - true_fop).induced_norm(), 0)
    assert np.isclose(of.normal_ordered(fop4 - true_fop).induced_norm(), 0)
Esempio n. 11
0
def test_random_evolution():
    sdim = 2
    nele = 2
    generator = generate_antisymm_generator(2 * sdim)
    nso = generator.shape[0]
    for p, q, r, s in product(range(nso), repeat=4):
        if p < q and s < r:
            assert np.isclose(generator[p, q, r, s], -generator[q, p, r, s])

    generator_mat = np.reshape(np.transpose(generator, [0, 3, 1, 2]),
                               (nso**2, nso**2)).astype(np.float)
    _, sigma, _ = np.linalg.svd(generator_mat)

    ul, vl, _, ul_ops, vl_ops, _ = \
        doubles_factorization_svd(generator)

    rwf = fqe.get_number_conserving_wavefunction(nele, sdim)
    # rwf = fqe.Wavefunction([[nele, 0, sdim]])
    rwf.set_wfn(strategy='random')
    rwf.normalize()

    sigma_idx = np.where(sigma > 1.0E-13)[0]
    for ll in sigma_idx:
        Smat = ul[ll] + vl[ll]
        Dmat = ul[ll] - vl[ll]

        S = ul_ops[ll] + vl_ops[ll]
        D = ul_ops[ll] - vl_ops[ll]
        op1 = S + 1j * of.hermitian_conjugated(S)
        op2 = S - 1j * of.hermitian_conjugated(S)
        op3 = D + 1j * of.hermitian_conjugated(D)
        op4 = D - 1j * of.hermitian_conjugated(D)

        op1mat = Smat + 1j * Smat.T
        op2mat = Smat - 1j * Smat.T
        op3mat = Dmat + 1j * Dmat.T
        op4mat = Dmat - 1j * Dmat.T

        o1_rwf = rwf.time_evolve(1 / 16, 1j * op1**2)
        ww, vv = np.linalg.eig(op1mat)
        assert np.allclose(vv @ np.diag(ww) @ vv.conj().T, op1mat)
        trwf = evolve_fqe_givens_unrestricted(rwf, vv.conj().T)
        v_pq = np.outer(ww, ww)
        for p, q in product(range(nso), repeat=2):
            fop = of.FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)),
                                     coefficient=-v_pq[p, q].imag)
            trwf = trwf.time_evolve(1 / 16, fop)
        trwf = evolve_fqe_givens_unrestricted(trwf, vv)
        assert np.isclose(fqe.vdot(o1_rwf, trwf), 1)

        o_rwf = rwf.time_evolve(1 / 16, 1j * op2**2)
        ww, vv = np.linalg.eig(op2mat)
        assert np.allclose(vv @ np.diag(ww) @ vv.conj().T, op2mat)
        trwf = evolve_fqe_givens_unrestricted(rwf, vv.conj().T)
        v_pq = np.outer(ww, ww)
        for p, q in product(range(nso), repeat=2):
            fop = of.FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)),
                                     coefficient=-v_pq[p, q].imag)
            trwf = trwf.time_evolve(1 / 16, fop)
        trwf = evolve_fqe_givens_unrestricted(trwf, vv)
        assert np.isclose(fqe.vdot(o_rwf, trwf), 1)

        o_rwf = rwf.time_evolve(-1 / 16, 1j * op3**2)
        ww, vv = np.linalg.eig(op3mat)
        assert np.allclose(vv @ np.diag(ww) @ vv.conj().T, op3mat)
        trwf = evolve_fqe_givens_unrestricted(rwf, vv.conj().T)
        v_pq = np.outer(ww, ww)
        for p, q in product(range(nso), repeat=2):
            fop = of.FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)),
                                     coefficient=-v_pq[p, q].imag)
            trwf = trwf.time_evolve(-1 / 16, fop)
        trwf = evolve_fqe_givens_unrestricted(trwf, vv)
        assert np.isclose(fqe.vdot(o_rwf, trwf), 1)

        o_rwf = rwf.time_evolve(-1 / 16, 1j * op4**2)
        ww, vv = np.linalg.eig(op4mat)
        assert np.allclose(vv @ np.diag(ww) @ vv.conj().T, op4mat)
        trwf = evolve_fqe_givens_unrestricted(rwf, vv.conj().T)
        v_pq = np.outer(ww, ww)
        for p, q in product(range(nso), repeat=2):
            fop = of.FermionOperator(((p, 1), (p, 0), (q, 1), (q, 0)),
                                     coefficient=-v_pq[p, q].imag)
            trwf = trwf.time_evolve(-1 / 16, fop)
        trwf = evolve_fqe_givens_unrestricted(trwf, vv)
        assert np.isclose(fqe.vdot(o_rwf, trwf), 1)
    def test_build_hamiltonian_paths(self):
        """Check that all cases of hamiltonian objects are built
        """
        self.assertRaises(TypeError, build_hamiltonian, 0)
        with self.subTest(name='general'):
            ops = FermionOperator('1^ 4^ 0 3', 1.0) \
                  + FermionOperator('0^ 5^ 3 2^ 4^ 1 7 6', 1.2) \
                  + FermionOperator('1^ 6', -0.3)
            ops += hermitian_conjugated(ops)
            self.assertIsInstance(build_hamiltonian(ops),
                                  general_hamiltonian.General)

        with self.subTest(name='sparse'):
            ops = FermionOperator('5^ 1^ 3^ 2 0 1', 1.0 - 1.j) \
                  + FermionOperator('1^ 0^ 2^ 3 1 5', 1.0 + 1.j)
            self.assertIsInstance(build_hamiltonian(ops),
                                  sparse_hamiltonian.SparseHamiltonian)

        with self.subTest(name='diagonal'):
            ops = FermionOperator('1^ 1', 1.0) \
                  + FermionOperator('2^ 2', 2.0) \
                  + FermionOperator('3^ 3', 3.0) \
                  + FermionOperator('4^ 4', 4.0)
            self.assertIsInstance(build_hamiltonian(ops),
                                  diagonal_hamiltonian.Diagonal)

        with self.subTest(name='gso'):
            ops = FermionOperator()
            for i in range(4):
                for j in range(4):
                    opstr = str(i) + '^ ' + str(j)
                    coeff = complex((i + 1) * (j + 1) * 0.1)
                    ops += FermionOperator(opstr, coeff)
            self.assertIsInstance(build_hamiltonian(ops),
                                  gso_hamiltonian.GSOHamiltonian)

        with self.subTest(name='restricted'):
            ops = FermionOperator()
            for i in range(0, 3, 2):
                for j in range(0, 3, 2):
                    coeff = complex((i + 1) * (j + 1) * 0.1)
                    opstr = str(i) + '^ ' + str(j)
                    ops += FermionOperator(opstr, coeff)
                    opstr = str(i + 1) + '^ ' + str(j + 1)
                    ops += FermionOperator(opstr, coeff)
            self.assertIsInstance(build_hamiltonian(ops),
                                  restricted_hamiltonian.RestrictedHamiltonian)

        with self.subTest(name='sso'):
            ops = FermionOperator()
            for i in range(0, 3, 2):
                for j in range(0, 3, 2):
                    coeff = complex((i + 1) * (j + 1) * 0.1)
                    opstr = str(i) + '^ ' + str(j)
                    ops += FermionOperator(opstr, coeff)
                    opstr = str(i + 1) + '^ ' + str(j + 1)
                    coeff *= 1.5
                    ops += FermionOperator(opstr, coeff)
            self.assertIsInstance(build_hamiltonian(ops),
                                  sso_hamiltonian.SSOHamiltonian)

        with self.subTest(name='diagonal_coulomb'):
            ops = FermionOperator()
            for i in range(4):
                for j in range(4):
                    opstring = str(i) + '^ ' + str(j) + '^ ' + str(
                        i) + ' ' + str(j)
                    ops += FermionOperator(opstring, 0.001 * (i + 1) * (j + 1))
            self.assertIsInstance(build_hamiltonian(ops),
                                  diagonal_coulomb.DiagonalCoulomb)
Esempio n. 13
0
def multi_particle_ucc(h, reference=0, trotter_order=1, trotter_steps=1):
    """
    UCC-style ansatz that doesn't preserve anything (i.e. uses all basis
    states). This is basically an implementation of create_arbitrary_state
    (however, theta will not match the coefficients in the superposition)
    built on pyquil.paulis.exponentiate_map.

    One idea is to use lowering and raising operators and "check operators"
    instead of Xs. This results in a more straight-forward mapping of theta
    to the coefficients since no previously produced state will be mapped
    to other states in a later stage. To clarify: with the current
    implementation the state |1 1 0> will both be produced by exp(X2 X1)
    operating on |0 0 0> and by exp(X2) operating on exp(X1)|0 0 0> (which
    contains a |0 1 0> term). This could improve potential bad properties
    with the current implementation. However, it might be difficult to
    create commuting hermitian terms, which is required in
    exponential_map_commuting_pauli_terms.

    If this function is called multiple times, particularly if theta has the
    same length in all calls, caching terms might significantly increase
    performance.

    @author: Joel

    :param np.ndarray h: The hamiltonian matrix.
    :param reference: integer representation of reference state
    :param trotter_order: Trotter order in trotterization
    :param trotter_steps: Trotter steps in trotterization
    :return: function(theta) which returns the ansatz Program. -1j*theta[i] is
        the coefficient in front of the term prod_k X_k^bit(i,k) where
        bit(i, k) is the k'th bit of i in binary, in the exponent.
    """
    dim = h.shape[0]

    terms = []
    for state in range(dim):
        if state != reference:
            term = QubitOperator(())
            for qubit in range(int.bit_length(state)):
                if (state ^ reference) & (1 << qubit):
                    # lower/raise qubit
                    term *= QubitOperator((qubit, "X"), 1 / 2) \
                            + QubitOperator((qubit, "Y"), 1j * (int(
                                reference & (1 << qubit) != 0) - 1 / 2))
                else:
                    # check that qubit has correct value (same as i and j)
                    term *= QubitOperator((), 1 / 2) \
                            + QubitOperator((qubit, "Z"), 1 / 2 - int(
                                reference & (1 << qubit) != 0))
            terms.append(
                qubitop_to_pyquilpauli(term - hermitian_conjugated(term)))

    exp_maps = trotterize(terms, trotter_order, trotter_steps)

    def wrap(theta):
        """
        Returns the ansatz Program.

        :param np.ndarray theta: parameters
        :return: the Program
        :rtype: pyquil.Program
        """
        prog = Program()
        for qubit in range(int.bit_length(reference)):
            if reference & (1 << qubit):
                prog += X(qubit)
        for idx, exp_map in enumerate(exp_maps):
            for exp in exp_map:
                prog += exp(theta[idx])
        return prog

    return wrap
Esempio n. 14
0
def test_operators_initialization(coefficient: float):
    # 1. Let's create some basic operator

    operator = FermionOperator('2^ 1 3 7^', coefficient)
    alternative_operator = coefficient * FermionOperator(((2, 1), (1, 0), (3, 0), (6, 1)))

    sum_of_operators = operator + alternative_operator

    identity = FermionOperator('')
    zero = FermionOperator()

    # print(f'Fermion operator: {operator}; the terms are {list(operator.terms.keys())[0]}')
    print(f'Fermion operator: {operator}; the flattened terms are {operator.flattened_terms}')
    print(f'Alternative fermion operator: {alternative_operator}')
    print(f'Sum fermion operator: {sum_of_operators}; the terms are {sum_of_operators.terms}')
    # print(operator == alternative_operator)
    print(f'Identity operator: {identity}')
    print(f'Zero operator: {zero}')

    # 2. Checking operator combinations

    lhs_operator = FermionOperator('17^')
    rhs_operator = FermionOperator('19^')

    print(describe_result(lhs_operator, rhs_operator, lambda lhs, rhs: lhs * rhs, '*'))
    print(describe_result(lhs_operator, lhs_operator, lambda lhs, rhs: lhs * rhs, '*'))

    print(describe_result(lhs_operator, rhs_operator, lambda lhs, rhs: lhs + rhs, '+'))
    print(describe_result(lhs_operator, 5, lambda lhs, rhs: lhs ** rhs, '^'))

    # print(describe_result(lhs_operator * rhs_operator, lhs_operator, lambda lhs, rhs: lhs / rhs, '/')) # Throws an exception

    # 3. Use some auxiliary functions

    print(f'Operator {lhs_operator ** 5} occupies {count_qubits(lhs_operator)} qubits')
    print(f'Operator {rhs_operator ** 2} occupies {count_qubits(rhs_operator)} qubits')
    print(f'Operator {operator} occupies {count_qubits(operator)} qubits')

    print(f'Operator {operator} is {"not " if not operator.is_normal_ordered() else ""}normal ordered')
    print(f'Operator {(lhs_operator + rhs_operator)} is {"not " if not (lhs_operator + rhs_operator).is_normal_ordered() else ""}normal ordered')
    print(f'Operator {(lhs_operator * rhs_operator)} is {"not " if not (lhs_operator * rhs_operator).is_normal_ordered() else ""}normal ordered')
    print(f'Operator {(rhs_operator * lhs_operator)} is {"not " if not (rhs_operator * lhs_operator).is_normal_ordered() else ""}normal ordered')

    print(f'Commutator of {lhs_operator} and {rhs_operator} is {commutator(lhs_operator, rhs_operator)}')

    # 4. See qubit operators

    operator = QubitOperator('X1 Z3', coefficient) + QubitOperator('X20', coefficient - 1)
    print(f'Operator {operator} occupies {count_qubits(operator)} qubits')

    # 4. Perform transformations from fermion to qubit operators

    fermion_operator = FermionOperator_('5^', 0.1 + 0.2j)
    fermion_operator += hermitian_conjugated(fermion_operator)
    jw_operator = jordan_wigner(fermion_operator)
    bk_operator = bravyi_kitaev(fermion_operator)

    print()
    print('Source fermion operator:')
    print(fermion_operator)
    print()
    print('Source fermion operator with conjugate:')
    print(fermion_operator)
    print()
    print('Source fermion operator with conjugate passed through Jordan-Wigner transformation:')
    print(jw_operator)
    print()
    print('Eigenspectrum of the obtained operator:')
    print(eigenspectrum(jw_operator))
    print()
    print('Reversed:')
    print(reverse_jordan_wigner(jw_operator))
    print()
    print('Source fermion operator with conjugate passed through Bravyi-Kitaev transformation:')
    print(bk_operator)
    print()
    print('Eigenspectrum of the obtained operator:')
    print(eigenspectrum(bk_operator))
    print()
Esempio n. 15
0
def doubles_factorization_svd(generator_tensor: np.ndarray, eig_cutoff=None):
    """
    Given an antisymmetric antihermitian tensor perform a double factorized
    low-rank decomposition.

    Given:

    A = sum_{pqrs}A^{pq}_{sr}p^ q^ r s

    with A^{pq}_{sr} = -A^{qp}_{sr} = -A^{pq}_{rs} = -A^{sr}_{pq}

    Rewrite A as a sum-of squares s.t

    A = sum_{l}Y_{l}^2

    where Y_{l} are normal operator one-body operators such that the spectral
    theorem holds and we can use the double factorization to implement an
    approximate evolution.
    """
    if not np.allclose(generator_tensor.imag, 0):
        raise TypeError("generator_tensor must be a real matrix")

    if eig_cutoff is not None:
        if eig_cutoff % 2 != 0:
            raise ValueError("eig_cutoff must be an even number")

    nso = generator_tensor.shape[0]
    generator_tensor = generator_tensor.real
    generator_mat = np.zeros((nso**2, nso**2))
    for row_gem, col_gem in product(range(nso**2), repeat=2):
        p, s = row_gem // nso, row_gem % nso
        q, r = col_gem // nso, col_gem % nso
        generator_mat[row_gem, col_gem] = generator_tensor[p, q, r, s]
    test_generator_mat = np.reshape(
        np.transpose(generator_tensor, [0, 3, 1, 2]),
        (nso**2, nso**2)).astype(np.float)

    assert np.allclose(test_generator_mat, generator_mat)

    if not np.allclose(generator_mat, generator_mat.T):
        raise ValueError("generator tensor does not correspond to four-fold"
                         " antisymmetry")

    one_body_residual = -np.einsum('pqrq->pr', generator_tensor)
    u, sigma, vh = np.linalg.svd(generator_mat)

    ul = []
    ul_ops = []
    vl = []
    vl_ops = []
    if eig_cutoff is None:
        max_sigma = len(sigma)
    else:
        max_sigma = eig_cutoff

    for ll in range(max_sigma):
        ul.append(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso)))
        ul_ops.append(
            get_fermion_op(np.sqrt(sigma[ll]) * u[:, ll].reshape((nso, nso))))
        vl.append(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso)))
        vl_ops.append(
            get_fermion_op(np.sqrt(sigma[ll]) * vh[ll, :].reshape((nso, nso))))
        S = ul_ops[ll] + vl_ops[ll]
        D = ul_ops[ll] - vl_ops[ll]
        op1 = S + 1j * of.hermitian_conjugated(S)
        op2 = S - 1j * of.hermitian_conjugated(S)
        op3 = D + 1j * of.hermitian_conjugated(D)
        op4 = D - 1j * of.hermitian_conjugated(D)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op1, of.hermitian_conjugated(op1))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op2, of.hermitian_conjugated(op2))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op3, of.hermitian_conjugated(op3))).induced_norm(), 0)
        assert np.isclose(
            of.normal_ordered(of.commutator(
                op4, of.hermitian_conjugated(op4))).induced_norm(), 0)

    one_body_op = of.FermionOperator()
    for p, q in product(range(nso), repeat=2):
        tfop = ((p, 1), (q, 0))
        one_body_op += of.FermionOperator(tfop,
                                          coefficient=one_body_residual[p, q])

    return ul, vl, one_body_residual, ul_ops, vl_ops, one_body_op