def test_neg(): op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) -op # out of place assert op.isclose(QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5)) correct = -1.0 * op assert correct.isclose(-op)
def test_imul_qubit_op(): op1 = QubitOperator(((0, 'Y'), (3, 'X'), (8, 'Z'), (11, 'X')), 3.j) op2 = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) op1 *= op2 correct_term = ((0, 'Y'), (1, 'X'), (3, 'Z'), (11, 'X')) assert len(op1.terms) == 1 assert correct_term in op1.terms
def test_str_multiple_terms(): op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) op += QubitOperator(((1, 'Y'), (3, 'Y'), (8, 'Z')), 0.6) assert (str(op) == "0.5 X1 Y3 Z8 +\n0.6 Y1 Y3 Z8" or str(op) == "0.6 Y1 Y3 Z8 +\n0.5 X1 Y3 Z8") op2 = QubitOperator((), 2) assert str(op2) == "2 I"
def test_imul_qubit_op_2(): op3 = QubitOperator(((1, 'Y'), (0, 'X')), -1j) op4 = QubitOperator(((1, 'Y'), (0, 'X'), (2, 'Z')), -1.5) op3 *= op4 op4 *= op3 assert ((2, 'Z'), ) in op3.terms assert op3.terms[((2, 'Z'), )] == 1.5j
def test_mul_multiple_terms(): op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) op += QubitOperator(((1, 'Z'), (3, 'X'), (8, 'Z')), 1.2) op += QubitOperator(((1, 'Z'), (3, 'Y'), (9, 'Z')), 1.4j) res = op * op correct = QubitOperator((), 0.5**2 + 1.2**2 + 1.4j**2) correct += QubitOperator(((1, 'Y'), (3, 'Z')), 2j * 1j * 0.5 * 1.2) assert res == correct
def test_mul_out_of_place(): op1 = QubitOperator(((0, 'Y'), (3, 'X'), (8, 'Z'), (11, 'X')), 3.j) op2 = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) op3 = op1 * op2 correct_coefficient = 1.j * 3.0j * 0.5 correct_term = ((0, 'Y'), (1, 'X'), (3, 'Z'), (11, 'X')) assert op1 == QubitOperator(((0, 'Y'), (3, 'X'), (8, 'Z'), (11, 'X')), 3.j) assert op2 == QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) assert op3 == QubitOperator(correct_term, correct_coefficient)
def test_imul_bidir(): op_a = QubitOperator(((1, 'Y'), (0, 'X')), -1j) op_b = QubitOperator(((1, 'Y'), (0, 'X'), (2, 'Z')), -1.5) op_a *= op_b op_b *= op_a assert ((2, 'Z'), ) in op_a.terms assert op_a.terms[((2, 'Z'), )] == 1.5j assert ((0, 'X'), (1, 'Y')) in op_b.terms assert op_b.terms[((0, 'X'), (1, 'Y'))] == -2.25j
def test_get_operators(): """Tests get_operators() with an operator with two terms.""" operator_00 = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 1) operator_01 = QubitOperator(((2, 'Z'), (3, 'Y')), 1) sum_operator = operator_00 + operator_01 operators = list(sum_operator.get_operators()) assert operators in [[operator_00, operator_01], [operator_01, operator_00]]
def test_itruediv_and_idiv(divisor): op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) op2 = copy.deepcopy(op) original = copy.deepcopy(op) correct = op * (1. / divisor) op /= divisor op2.__idiv__(divisor) # To test python 2 version as well assert op.isclose(correct) assert op2.isclose(correct) # Test if done in-place assert not op.isclose(original) assert not op2.isclose(original)
def test_truediv_and_div(divisor): op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 0.5) op2 = copy.deepcopy(op) original = copy.deepcopy(op) res = op / divisor res2 = op2.__div__(divisor) # To test python 2 version as well correct = op * (1. / divisor) assert res.isclose(correct) assert res2.isclose(correct) # Test if done out of place assert op.isclose(original) assert op2.isclose(original)
def test_isub_different_term(): term_a = ((1, 'X'), (3, 'Y'), (8, 'Z')) term_b = ((1, 'Z'), (3, 'Y'), (8, 'Z')) a = QubitOperator(term_a, 1.0) a -= QubitOperator(term_b, 0.5) assert len(a.terms) == 2 assert a.terms[term_a] == pytest.approx(1.0) assert a.terms[term_b] == pytest.approx(-0.5) a -= QubitOperator(term_b, 0.5) assert len(a.terms) == 2 assert a.terms[term_a] == pytest.approx(1.0) assert a.terms[term_b] == pytest.approx(-1.0)
def test_sub(): term_a = ((1, 'X'), (3, 'Y'), (8, 'Z')) term_b = ((1, 'Z'), (3, 'Y'), (8, 'Z')) a = QubitOperator(term_a, 1.0) b = QubitOperator(term_b, 0.5) res = a - b assert len(res.terms) == 2 assert res.terms[term_a] == pytest.approx(1.0) assert res.terms[term_b] == pytest.approx(-0.5) res2 = b - a assert len(res2.terms) == 2 assert res2.terms[term_a] == pytest.approx(-1.0) assert res2.terms[term_b] == pytest.approx(0.5)
def test_isclose_different_terms(): a = QubitOperator(((1, 'Y'),), -0.1j) b = QubitOperator(((1, 'X'),), -0.1j) assert a.isclose(b, rel_tol=1e-12, abs_tol=0.2) assert not a.isclose(b, rel_tol=1e-12, abs_tol=0.05) assert b.isclose(a, rel_tol=1e-12, abs_tol=0.2) assert not b.isclose(a, rel_tol=1e-12, abs_tol=0.05)
def test_integration_observable_to_vqe_cost(monkeypatch, mol_name, terms_ref, expected_cost, custom_wires, tol): r"""Test if `convert_observable()` in qchem integrates with `ExpvalCost()` in pennylane""" qOp = QubitOperator() if terms_ref is not None: monkeypatch.setattr(qOp, "terms", terms_ref) vqe_observable = qchem.convert_observable(qOp, custom_wires) num_qubits = len(vqe_observable.wires) assert vqe_observable.terms.__repr__() # just to satisfy codecov if custom_wires is None: wires = num_qubits elif isinstance(custom_wires, dict): wires = qchem.structure._process_wires(custom_wires) else: wires = custom_wires[:num_qubits] dev = qml.device("default.qubit", wires=wires) # can replace the ansatz with more suitable ones later. def dummy_ansatz(phis, wires): for phi, w in zip(phis, wires): qml.RX(phi, wires=w) dummy_cost = qml.ExpvalCost(dummy_ansatz, vqe_observable, dev) params = [0.1 * i for i in range(num_qubits)] res = dummy_cost(params) assert np.allclose(res, expected_cost, **tol)
def test_integration_hamiltonian_to_vqe_cost(monkeypatch, mol_name, terms_ref, expected_cost, tol): r"""Test if `convert_hamiltonian()` in qchem integrates with `VQECost()` in pennylane""" qOp = QubitOperator() if terms_ref is not None: monkeypatch.setattr(qOp, "terms", terms_ref) vqe_hamiltonian = qchem.convert_hamiltonian(qOp) # maybe make num_qubits a @property of the Hamiltonian class? num_qubits = max( 1, len(set([w for op in vqe_hamiltonian.ops for w in op.wires]))) dev = qml.device("default.qubit", wires=num_qubits) print(vqe_hamiltonian.terms) # can replace the ansatz with more suitable ones later. def dummy_ansatz(phis, wires): for phi, w in zip(phis, wires): qml.RX(phi, wires=w) dummy_cost = qml.VQECost(dummy_ansatz, vqe_hamiltonian, dev) params = [0.1 * i for i in range(num_qubits)] res = dummy_cost(params) assert np.allclose(res, expected_cost, **tol)
def test_isclose_rel_tol(): a = QubitOperator('X0', 1) b = QubitOperator('X0', 2) assert a.isclose(b, rel_tol=2.5, abs_tol=0.1) # Test symmetry assert a.isclose(b, rel_tol=1, abs_tol=0.1) assert b.isclose(a, rel_tol=1, abs_tol=0.1)
def _terms_to_qubit_operator(coeffs, ops): r"""Converts a 2-tuple of complex coefficients and PennyLane operations to OpenFermion ``QubitOperator``. This function is the inverse of ``_qubit_operator_to_terms``. Args: coeffs (array[complex]): coefficients for each observable, same length as ops ops (Iterable[pennylane.operation.Observable]): List of PennyLane observables as Tensor products of Pauli observables Returns: QubitOperator: an instance of OpenFermion's ``QubitOperator``. """ q_op = QubitOperator() for coeff, op in zip(coeffs, ops): # wire ids wires = [js[0] for js in op.wires] # Pauli axis names, note s[-1] expects only 'Pauli{X,Y,Z}' pauli_names = [s[-1] for s in op.name] extra_obsvbs = set( op.name) - {"PauliX", "PauliY", "PauliZ", "Identity"} if extra_obsvbs != set(): raise ValueError( "Expected only PennyLane observables PauliX/Y/Z or Identity, " + "but also got {}.".format(extra_obsvbs)) if op.name == ["Identity"] and wires == [0]: term_str = "" else: term_str = " ".join([ "{}{}".format(pauli, wire) for pauli, wire in zip(pauli_names, wires) ]) # This is how one makes QubitOperator in OpenFermion q_op += coeff * QubitOperator(term_str) return q_op
def test_add(): term_a = ((1, 'X'), (3, 'Y'), (8, 'Z')) term_b = ((1, 'Z'), (3, 'Y'), (8, 'Z')) a = QubitOperator(term_a, 1.0) b = QubitOperator(term_b, 0.5) res = a + b + b assert len(res.terms) == 2 assert res.terms[term_a] == pytest.approx(1.0) assert res.terms[term_b] == pytest.approx(1.0) # Test out of place assert a.isclose(QubitOperator(term_a, 1.0)) assert b.isclose(QubitOperator(term_b, 0.5))
def test_renormalize(): op = QubitOperator(((1, 'X'), (3, 'Y'), (8, 'Z')), 1) op += QubitOperator(((2, 'Z'), (3, 'Y')), 1) op.renormalize() for term in op.terms: assert op.terms[term] == pytest.approx(1 / numpy.sqrt(2.)) assert op.induced_norm(2) == pytest.approx(1.)
def test_observable(me_table, init_term, mapping, terms_exp, monkeypatch): r"""Tests the correctness of the 'observable' function used to build many-body observables. The parametrized inputs `terms_exp` are `.terms` attribute of the corresponding `QubitOperator. The equality checking is implemented in the `qchem` module itself as it could be something useful to the users as well. """ res_obs = qchem.observable(me_table, init_term=init_term, mapping=mapping) qubit_op = QubitOperator() monkeypatch.setattr(qubit_op, "terms", terms_exp) assert qchem._qubit_operators_equivalent(qubit_op, res_obs)
def test_particle_number_observable(n_orbitals, mapping, terms_exp, monkeypatch): r"""Tests the correctness of the particle number observable :math:`\hat{N}` generated by the ``'particle_number'`` function. The parametrized inputs are `.terms` attribute of the particle number `QubitOperator`. The equality checking is implemented in the `qchem` module itself as it could be something useful to the users as well. """ N = qchem.particle_number(n_orbitals, mapping=mapping) particle_number_qubit_op = QubitOperator() monkeypatch.setattr(particle_number_qubit_op, "terms", terms_exp) assert qchem._qubit_operators_equivalent(particle_number_qubit_op, N)
def test_spin_z(orbitals, mapping, terms_exp, monkeypatch): r"""Tests the correctness of the :math:`\hat{S}_z` observable built by the function `'spin_z'`. The parametrized inputs are `.terms` attribute of the `QubitOperator. The equality checking is implemented in the `qchem` module itself as it could be something useful to the users as well. """ Sz = qchem.spin_z(orbitals, mapping=mapping) Sz_qubit_op = QubitOperator() monkeypatch.setattr(Sz_qubit_op, "terms", terms_exp) assert qchem._qubit_operators_equivalent(Sz_qubit_op, Sz)
def test_hamiltonian_conversion(mol_name, terms_ref, monkeypatch): r"""Test the correctness of the QubitOperator Hamiltonian conversion from OpenFermion to Pennylane. The parametrized inputs are `.terms` attribute of the output `QubitOperator`s based on the same set of test molecules as `test_gen_hamiltonian_pauli_basis`. The equality checking is implemented in the `qchem` module itself as it could be something useful to the users as well. """ qOp = QubitOperator() if terms_ref is not None: monkeypatch.setattr(qOp, "terms", terms_ref) vqe_hamiltonian = qchem.convert_hamiltonian(qOp) assert qchem._qubit_operators_equivalent(qOp, vqe_hamiltonian)
def test_build_s2_observable(mol_name, n_act_elect, n_act_orb, mapping, terms_exp, monkeypatch): r"""Tests the correctness of the built total-spin observable. The parametrized inputs are `.terms` attribute of the total spin `QubitOperator. The equality checking is implemented in the `qchem` module itself as it could be something useful to the users as well. """ s2_me_table, init_term = qchem.get_spin2_matrix_elements( mol_name, ref_dir, n_active_electrons=n_act_elect, n_active_orbitals=n_act_orb ) s2_obs = qchem.observable(s2_me_table, init_term=init_term, mapping=mapping) s2_qubit_op = QubitOperator() monkeypatch.setattr(s2_qubit_op, "terms", terms_exp) assert qchem._qubit_operators_equivalent(s2_qubit_op, s2_obs)
def test_observable_conversion(mol_name, terms_ref, custom_wires, monkeypatch): r"""Test the correctness of the QubitOperator observable conversion from OpenFermion to Pennylane. The parametrized inputs are `.terms` attribute of the output `QubitOperator`s based on the same set of test molecules as `test_gen_hamiltonian_pauli_basis`. The equality checking is implemented in the `qchem` module itself as it could be something useful to the users as well. """ qOp = QubitOperator() if terms_ref is not None: monkeypatch.setattr(qOp, "terms", terms_ref) vqe_observable = qchem.convert_observable(qOp, custom_wires) if isinstance(custom_wires, dict): custom_wires = {v: k for k, v in custom_wires.items()} assert qchem._qubit_operators_equivalent(qOp, vqe_observable, custom_wires)
def test_mul_by_scalarzero(): op = QubitOperator(((1, 'Y'), (0, 'X')), -1j) * 0 assert ((0, 'X'), (1, 'Y')) in op.terms assert op.terms[((0, 'X'), (1, 'Y'))] == pytest.approx(0.0)
def test_imul_bad_multiplier(): op = QubitOperator(((1, 'Y'), (0, 'X')), -1j) with pytest.raises(TypeError): op *= "1"
def test_renormalize_error(): op = QubitOperator() with pytest.raises(ZeroDivisionError): op.renormalize()
def test_imul_inplace(): qubit_op = QubitOperator("X1") prev_id = id(qubit_op) qubit_op *= 3. assert id(qubit_op) == prev_id
def test_imul_scalar(multiplier): loc_op = ((1, 'X'), (2, 'Y')) qubit_op = QubitOperator(loc_op) qubit_op *= multiplier assert qubit_op.terms[loc_op] == pytest.approx(multiplier)