diff --git a/src/qibo/hamiltonians/hamiltonians.py b/src/qibo/hamiltonians/hamiltonians.py index 578d63b5da..7bf98865bf 100644 --- a/src/qibo/hamiltonians/hamiltonians.py +++ b/src/qibo/hamiltonians/hamiltonians.py @@ -781,6 +781,9 @@ def __mul__(self, o): new_ham.constant = self.constant * o if self._dense is not None: new_ham.dense = o * self._dense + + new_ham.nqubits = self.nqubits + return new_ham def apply_gates(self, state, density_matrix=False): diff --git a/tests/test_hamiltonians_symbolic.py b/tests/test_hamiltonians_symbolic.py index d1d8485703..0a0f225728 100644 --- a/tests/test_hamiltonians_symbolic.py +++ b/tests/test_hamiltonians_symbolic.py @@ -1,13 +1,13 @@ -"""Test methods of :class:`qibo.core.hamiltonians.SymbolicHamiltonian`.""" +"""Test methods of :class:`qibo.core.SymbolicHamiltonian`.""" import numpy as np import pytest import sympy -from pytest import approx -from qibo import Circuit, gates, hamiltonians +from qibo import Circuit, gates +from qibo.hamiltonians import TFIM, XXZ, Hamiltonian, SymbolicHamiltonian from qibo.quantum_info.random_ensembles import random_density_matrix, random_statevector -from qibo.symbols import I, Y, Z +from qibo.symbols import I, Symbol, X, Y, Z def symbolic_tfim(nqubits, h=1.0): @@ -22,26 +22,20 @@ def symbolic_tfim(nqubits, h=1.0): def test_symbolic_hamiltonian_errors(backend): # Wrong type of Symbol matrix - from qibo.symbols import Symbol - with pytest.raises(TypeError): s = Symbol(0, "test") # Wrong type of symbolic expression with pytest.raises(TypeError): - ham = hamiltonians.SymbolicHamiltonian("test", backend=backend) + ham = SymbolicHamiltonian("test", backend=backend) # Passing form with symbol that is not in ``symbol_map`` from qibo import matrices Z, X = sympy.Symbol("Z"), sympy.Symbol("X") symbol_map = {Z: (0, matrices.Z)} with pytest.raises(ValueError): - ham = hamiltonians.SymbolicHamiltonian( - Z * X, symbol_map=symbol_map, backend=backend - ) + ham = SymbolicHamiltonian(Z * X, symbol_map=symbol_map, backend=backend) # Invalid operation in Hamiltonian expresion - ham = hamiltonians.SymbolicHamiltonian( - sympy.cos(Z), symbol_map=symbol_map, backend=backend - ) + ham = SymbolicHamiltonian(sympy.cos(Z), symbol_map=symbol_map, backend=backend) with pytest.raises(TypeError): dense = ham.dense @@ -49,10 +43,8 @@ def test_symbolic_hamiltonian_errors(backend): @pytest.mark.parametrize("nqubits", [3, 4]) @pytest.mark.parametrize("calcterms", [False, True]) def test_symbolictfim_hamiltonian_to_dense(backend, nqubits, calcterms): - final_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1), backend=backend - ) - target_ham = hamiltonians.TFIM(nqubits, h=1, backend=backend) + final_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1), backend=backend) + target_ham = TFIM(nqubits, h=1, backend=backend) if calcterms: _ = final_ham.terms backend.assert_allclose(final_ham.matrix, target_ham.matrix, atol=1e-15) @@ -61,14 +53,12 @@ def test_symbolictfim_hamiltonian_to_dense(backend, nqubits, calcterms): @pytest.mark.parametrize("nqubits", [3, 4]) @pytest.mark.parametrize("calcterms", [False, True]) def test_symbolicxxz_hamiltonian_to_dense(backend, nqubits, calcterms): - from qibo.symbols import X, Y, Z - sham = sum(X(i) * X(i + 1) for i in range(nqubits - 1)) sham += sum(Y(i) * Y(i + 1) for i in range(nqubits - 1)) sham += 0.5 * sum(Z(i) * Z(i + 1) for i in range(nqubits - 1)) sham += X(0) * X(nqubits - 1) + Y(0) * Y(nqubits - 1) + 0.5 * Z(0) * Z(nqubits - 1) - final_ham = hamiltonians.SymbolicHamiltonian(sham, backend=backend) - target_ham = hamiltonians.XXZ(nqubits, backend=backend) + final_ham = SymbolicHamiltonian(sham, backend=backend) + target_ham = XXZ(nqubits, backend=backend) if calcterms: _ = final_ham.terms backend.assert_allclose(final_ham.matrix, target_ham.matrix, atol=1e-15) @@ -79,10 +69,8 @@ def test_symbolicxxz_hamiltonian_to_dense(backend, nqubits, calcterms): @pytest.mark.parametrize("calcdense", [False, True]) def test_symbolic_hamiltonian_scalar_mul(backend, nqubits, calcterms, calcdense): """Test multiplication of Trotter Hamiltonian with scalar.""" - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - target_ham = 2 * hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + target_ham = 2 * TFIM(nqubits, h=1.0, backend=backend) if calcterms: _ = local_ham.terms if calcdense: @@ -90,9 +78,7 @@ def test_symbolic_hamiltonian_scalar_mul(backend, nqubits, calcterms, calcdense) local_dense = (2 * local_ham).dense backend.assert_allclose(local_dense.matrix, target_ham.matrix) - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) if calcterms: _ = local_ham.terms if calcdense: @@ -106,10 +92,8 @@ def test_symbolic_hamiltonian_scalar_mul(backend, nqubits, calcterms, calcdense) @pytest.mark.parametrize("calcdense", [False, True]) def test_symbolic_hamiltonian_scalar_add(backend, nqubits, calcterms, calcdense): """Test addition of Trotter Hamiltonian with scalar.""" - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - target_ham = 2 + hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + target_ham = 2 + TFIM(nqubits, h=1.0, backend=backend) if calcterms: _ = local_ham.terms if calcdense: @@ -117,9 +101,7 @@ def test_symbolic_hamiltonian_scalar_add(backend, nqubits, calcterms, calcdense) local_dense = (2 + local_ham).dense backend.assert_allclose(local_dense.matrix, target_ham.matrix) - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) if calcterms: _ = local_ham.terms if calcdense: @@ -133,10 +115,8 @@ def test_symbolic_hamiltonian_scalar_add(backend, nqubits, calcterms, calcdense) @pytest.mark.parametrize("calcdense", [False, True]) def test_symbolic_hamiltonian_scalar_sub(backend, nqubits, calcterms, calcdense): """Test subtraction of Trotter Hamiltonian with scalar.""" - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - target_ham = 2 - hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + target_ham = 2 - TFIM(nqubits, h=1.0, backend=backend) if calcterms: _ = local_ham.terms if calcdense: @@ -144,10 +124,8 @@ def test_symbolic_hamiltonian_scalar_sub(backend, nqubits, calcterms, calcdense) local_dense = (2 - local_ham).dense backend.assert_allclose(local_dense.matrix, target_ham.matrix) - target_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) - 2 - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) + target_ham = TFIM(nqubits, h=1.0, backend=backend) - 2 + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) if calcterms: _ = local_ham.terms if calcdense: @@ -163,12 +141,8 @@ def test_symbolic_hamiltonian_operator_add_and_sub( backend, nqubits, calcterms, calcdense ): """Test addition and subtraction between Trotter Hamiltonians.""" - local_ham1 = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - local_ham2 = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=0.5), backend=backend - ) + local_ham1 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + local_ham2 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=0.5), backend=backend) if calcterms: _ = local_ham1.terms _ = local_ham2.terms @@ -176,18 +150,14 @@ def test_symbolic_hamiltonian_operator_add_and_sub( _ = local_ham1.dense _ = local_ham2.dense local_ham = local_ham1 + local_ham2 - target_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + hamiltonians.TFIM( + target_ham = TFIM(nqubits, h=1.0, backend=backend) + TFIM( nqubits, h=0.5, backend=backend ) dense = local_ham.dense backend.assert_allclose(dense.matrix, target_ham.matrix) - local_ham1 = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - local_ham2 = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=0.5), backend=backend - ) + local_ham1 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + local_ham2 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=0.5), backend=backend) if calcterms: _ = local_ham1.terms _ = local_ham2.terms @@ -195,25 +165,37 @@ def test_symbolic_hamiltonian_operator_add_and_sub( _ = local_ham1.dense _ = local_ham2.dense local_ham = local_ham1 - local_ham2 - target_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) - hamiltonians.TFIM( + target_ham = TFIM(nqubits, h=1.0, backend=backend) - TFIM( nqubits, h=0.5, backend=backend ) dense = local_ham.dense backend.assert_allclose(dense.matrix, target_ham.matrix) + # Test multiplication and sum + target = XXZ(nqubits, backend=backend) + term_1 = SymbolicHamiltonian( + X(0) * X(1) + X(1) * X(2) + X(0) * X(2), backend=backend + ) + term_2 = SymbolicHamiltonian( + Y(0) * Y(1) + Y(1) * Y(2) + Y(0) * Y(2), backend=backend + ) + term_3 = SymbolicHamiltonian( + Z(0) * Z(1) + Z(1) * Z(2) + Z(0) * Z(2), backend=backend + ) + hamiltonian = term_1 + term_2 + 0.5 * term_3 + matrix = hamiltonian.dense.matrix + + backend.assert_allclose(matrix, target.matrix) + @pytest.mark.parametrize("nqubits", [5]) @pytest.mark.parametrize("calcterms", [False, True]) @pytest.mark.parametrize("calcdense", [False, True]) def test_symbolic_hamiltonian_hamiltonianmatmul(backend, nqubits, calcterms, calcdense): - local_ham1 = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - local_ham2 = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=0.5), backend=backend - ) - dense_ham1 = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) - dense_ham2 = hamiltonians.TFIM(nqubits, h=0.5, backend=backend) + local_ham1 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + local_ham2 = SymbolicHamiltonian(symbolic_tfim(nqubits, h=0.5), backend=backend) + dense_ham1 = TFIM(nqubits, h=1.0, backend=backend) + dense_ham2 = TFIM(nqubits, h=0.5, backend=backend) if calcterms: _ = local_ham1.terms _ = local_ham2.terms @@ -234,10 +216,8 @@ def test_symbolic_hamiltonian_matmul(backend, nqubits, density_matrix, calcterms if density_matrix else random_statevector(2**nqubits, backend=backend) ) - local_ham = hamiltonians.SymbolicHamiltonian( - symbolic_tfim(nqubits, h=1.0), backend=backend - ) - dense_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + dense_ham = TFIM(nqubits, h=1.0, backend=backend) if calcterms: _ = local_ham.terms local_matmul = local_ham @ state @@ -251,15 +231,12 @@ def test_symbolic_hamiltonian_matmul(backend, nqubits, density_matrix, calcterms def test_symbolic_hamiltonian_state_expectation( backend, nqubits, normalize, calcterms, calcdense ): - local_ham = ( - hamiltonians.SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) - + 2 - ) + local_ham = SymbolicHamiltonian(symbolic_tfim(nqubits, h=1.0), backend=backend) + 2 if calcterms: _ = local_ham.terms if calcdense: _ = local_ham.dense - dense_ham = hamiltonians.TFIM(nqubits, h=1.0, backend=backend) + 2 + dense_ham = TFIM(nqubits, h=1.0, backend=backend) + 2 state = random_statevector(2**nqubits, backend=backend) @@ -280,17 +257,17 @@ def test_symbolic_hamiltonian_state_expectation_different_nqubits( ): expr = symbolic_tfim(3, h=1.0) if give_nqubits: - local_ham = hamiltonians.SymbolicHamiltonian(expr, nqubits=5, backend=backend) + local_ham = SymbolicHamiltonian(expr, nqubits=5, backend=backend) else: - local_ham = hamiltonians.SymbolicHamiltonian(expr, backend=backend) + local_ham = SymbolicHamiltonian(expr, backend=backend) if calcterms: _ = local_ham.terms if calcdense: _ = local_ham.dense - dense_ham = hamiltonians.TFIM(3, h=1.0, backend=backend) + dense_ham = TFIM(3, h=1.0, backend=backend) dense_matrix = np.kron(backend.to_numpy(dense_ham.matrix), np.eye(4)) - dense_ham = hamiltonians.Hamiltonian(5, dense_matrix, backend=backend) + dense_ham = Hamiltonian(5, dense_matrix, backend=backend) state = random_statevector(2**5, backend=backend) @@ -314,8 +291,8 @@ def test_hamiltonian_expectation_from_samples(backend): backend.set_seed(0) obs0 = 2 * Z(0) * Z(1) + Z(0) * Z(2) obs1 = 2 * Z(0) * Z(1) + Z(0) * Z(2) * I(3) - h0 = hamiltonians.SymbolicHamiltonian(obs0, backend=backend) - h1 = hamiltonians.SymbolicHamiltonian(obs1, backend=backend) + h0 = SymbolicHamiltonian(obs0, backend=backend) + h1 = SymbolicHamiltonian(obs1, backend=backend) c = Circuit(4) c.add(gates.RX(0, np.random.rand())) c.add(gates.RX(1, np.random.rand())) @@ -337,7 +314,7 @@ def test_symbolic_hamiltonian_abstract_symbol_ev(backend, density_matrix, calcte matrix = np.random.random((2, 2)) form = X(0) * Symbol(1, matrix) + Symbol(0, matrix) * X(1) - local_ham = hamiltonians.SymbolicHamiltonian(form, backend=backend) + local_ham = SymbolicHamiltonian(form, backend=backend) if calcterms: _ = local_ham.terms @@ -353,8 +330,8 @@ def test_symbolic_hamiltonian_abstract_symbol_ev(backend, density_matrix, calcte def test_trotter_hamiltonian_operation_errors(backend): """Test errors in ``SymbolicHamiltonian`` addition and subtraction.""" - h1 = hamiltonians.SymbolicHamiltonian(symbolic_tfim(3, h=1.0), backend=backend) - h2 = hamiltonians.SymbolicHamiltonian(symbolic_tfim(4, h=1.0), backend=backend) + h1 = SymbolicHamiltonian(symbolic_tfim(3, h=1.0), backend=backend) + h2 = SymbolicHamiltonian(symbolic_tfim(4, h=1.0), backend=backend) with pytest.raises(RuntimeError): h = h1 + h2 with pytest.raises(RuntimeError): @@ -373,7 +350,7 @@ def test_trotter_hamiltonian_operation_errors(backend): h = h1 @ "test" with pytest.raises(NotImplementedError): h = h1 @ np.ones((2, 2, 2, 2)) - h2 = hamiltonians.XXZ(3, dense=False, backend=backend) + h2 = XXZ(3, dense=False, backend=backend) with pytest.raises(NotImplementedError): h = h1 @ h2 @@ -382,7 +359,9 @@ def test_symbolic_hamiltonian_with_constant(backend): c = Circuit(1) c.add(gates.H(0)) c.add(gates.M(0)) - h = hamiltonians.SymbolicHamiltonian(1e6 - Z(0), backend=backend) + h = SymbolicHamiltonian(1e6 - Z(0), backend=backend) result = c.execute(nshots=10000) - assert float(result.expectation_from_samples(h)) == approx(1e6, rel=1e-5, abs=0.0) + assert float(result.expectation_from_samples(h)) == pytest.approx( + 1e6, rel=1e-5, abs=0.0 + )