diff --git a/tests/test_cuquantum_cutensor_backend.py b/tests/test_cuquantum_cutensor_backend.py index 5130dd5..d5b2ffb 100644 --- a/tests/test_cuquantum_cutensor_backend.py +++ b/tests/test_cuquantum_cutensor_backend.py @@ -1,11 +1,8 @@ import math -from timeit import default_timer as timer - import cupy as cp -import numpy as np import pytest import qibo -from qibo import Circuit, construct_backend, gates, hamiltonians +from qibo import construct_backend, hamiltonians from qibo.models import QFT from qibo.symbols import X, Z @@ -16,14 +13,6 @@ def qibo_qft(nqubits, swaps): return circ_qibo, state_vec -def time(func): - start = timer() - res = func() - end = timer() - time = end - start - return time, res - - def build_observable(nqubits): """Helper function to construct a target observable.""" hamiltonian_form = 0 @@ -34,35 +23,64 @@ def build_observable(nqubits): return hamiltonian, hamiltonian_form -@pytest.mark.gpu +def build_observable_dict(nqubits): + """Construct a target observable as a dictionary representation. + + Returns a dictionary suitable for `create_hamiltonian_from_dict`. + """ + terms = [] + + for i in range(nqubits): + term = { + "coefficient": 0.5, + "operators": [("X", i % nqubits), ("Z", (i + 1) % nqubits)], + } + terms.append(term) + + return {"terms": terms} + + @pytest.mark.parametrize("nqubits", [1, 2, 5, 10]) def test_eval(nqubits: int, dtype="complex128"): - """Evaluate QASM with cuQuantum. - + """ Args: nqubits (int): Total number of qubits in the system. dtype (str): The data type for precision, 'complex64' for single, 'complex128' for double. """ - import qibotn.eval - # Test qibo - # qibo.set_backend(backend=config.qibo.backend, platform=config.qibo.platform) qibo.set_backend(backend="numpy") - qibo_time, (qibo_circ, result_sv) = time(lambda: qibo_qft(nqubits, swaps=True)) + qibo_circ, result_sv = qibo_qft(nqubits, swaps=True) result_sv_cp = cp.asarray(result_sv) - # Test Cuquantum - cutn_time, result_tn = time( - lambda: qibotn.eval.dense_vector_tn(qibo_circ, dtype).flatten() + # Test cutensornet + backend = construct_backend(backend="qibotn", platform="cutensornet") + # Test 1: no computation settings specified. Use default. + result_tn = backend.execute_circuit(circuit=qibo_circ) + print( + f"State vector difference: {abs(result_tn.statevector.flatten() - result_sv_cp).max():0.3e}" ) + assert cp.allclose( + result_sv_cp, result_tn.statevector.flatten() + ), "Resulting dense vectors do not match" - print(f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") - - assert cp.allclose(result_sv_cp, result_tn), "Resulting dense vectors do not match" + # Test 2: Explicit computation settings specified (same as default). + computation_settings = { + "MPI_enabled": False, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": False, + } + backend.configure_tn_simulation(computation_settings) + result_tn = backend.execute_circuit(circuit=qibo_circ) + print( + f"State vector difference: {abs(result_tn.statevector.flatten() - result_sv_cp).max():0.3e}" + ) + assert cp.allclose( + result_sv_cp, result_tn.statevector.flatten() + ), "Resulting dense vectors do not match" -@pytest.mark.gpu @pytest.mark.parametrize("nqubits", [2, 5, 10]) def test_mps(nqubits: int, dtype="complex128"): """Evaluate MPS with cuQuantum. @@ -72,41 +90,59 @@ def test_mps(nqubits: int, dtype="complex128"): dtype (str): The data type for precision, 'complex64' for single, 'complex128' for double. """ - import qibotn.eval # Test qibo qibo.set_backend(backend="numpy") - - qibo_time, (circ_qibo, result_sv) = time(lambda: qibo_qft(nqubits, swaps=True)) - + qibo_circ, result_sv = qibo_qft(nqubits, swaps=True) result_sv_cp = cp.asarray(result_sv) - # Test of MPS - gate_algo = { - "qr_method": False, - "svd_method": { - "partition": "UV", - "abs_cutoff": 1e-12, - }, + # Test cutensornet + backend = construct_backend(backend="qibotn", platform="cutensornet") + # Test 1: No MPS computation settings specified. Use default. + computation_settings_1 = { + "MPI_enabled": False, + "MPS_enabled": True, + "NCCL_enabled": False, + "expectation_enabled": False, } - - cutn_time, result_tn = time( - lambda: qibotn.eval.dense_vector_mps(circ_qibo, gate_algo, dtype).flatten() + backend.configure_tn_simulation(computation_settings_1) + result_tn = backend.execute_circuit(circuit=qibo_circ) + print( + f"State vector difference: {abs(result_tn.statevector.flatten() - result_sv_cp).max():0.3e}" ) + assert cp.allclose( + result_tn.statevector.flatten(), result_sv_cp + ), "Resulting dense vectors do not match" - print(f"State vector difference: {abs(result_tn - result_sv_cp).max():0.3e}") - - assert cp.allclose(result_tn, result_sv_cp), "Resulting dense vectors do not match" + # Test 2: Explicit MPS computation settings specified (same as default). + computation_settings_2 = { + "MPI_enabled": False, + "MPS_enabled": { + "qr_method": False, + "svd_method": { + "partition": "UV", + "abs_cutoff": 1e-12, + }, + }, + "NCCL_enabled": False, + "expectation_enabled": False, + } + backend.configure_tn_simulation(computation_settings_2) + result_tn = backend.execute_circuit(circuit=qibo_circ) + print( + f"State vector difference: {abs(result_tn.statevector.flatten() - result_sv_cp).max():0.3e}" + ) + assert cp.allclose( + result_tn.statevector.flatten(), result_sv_cp + ), "Resulting dense vectors do not match" -@pytest.mark.gpu @pytest.mark.parametrize("nqubits", [2, 5, 10]) def test_expectation(nqubits: int, dtype="complex128"): - import qibotn.eval - circ_qibo, state_vec_qibo = qibo_qft(nqubits, swaps=True) + # Test qibo + qibo_circ, state_vec_qibo = qibo_qft(nqubits, swaps=True) ham, ham_form = build_observable(nqubits) - numpy_backend = construct_backend("numpy") exact_expval = numpy_backend.calculate_expectation_state( hamiltonian=ham, @@ -114,6 +150,39 @@ def test_expectation(nqubits: int, dtype="complex128"): normalize=False, ) - tn_expval = qibotn.eval.expectation_tn(circ_qibo, dtype, ham).flatten() + # Test cutensornet + backend = construct_backend(backend="qibotn", platform="cutensornet") - assert math.isclose(exact_expval.item(), tn_expval.real.get().item(), abs_tol=1e-7) + # Test 1: No Hamilitonian computation settings specified. Use default. + computation_settings_1 = { + "MPI_enabled": False, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": True, + } + backend.configure_tn_simulation(computation_settings_1) + result_tn = backend.execute_circuit(circuit=qibo_circ) + assert math.isclose(exact_expval.item(), result_tn.real.get().item(), abs_tol=1e-7) + + # Test 2: hamiltonians.SymbolicHamiltonian object in computation settings specified. + computation_settings_2 = { + "MPI_enabled": False, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": ham, + } + backend.configure_tn_simulation(computation_settings_2) + result_tn = backend.execute_circuit(circuit=qibo_circ) + assert math.isclose(exact_expval.item(), result_tn.real.get().item(), abs_tol=1e-7) + + # Test 3: Dictionary object form of hamiltonian in computation settings specified. + ham_dict = build_observable_dict(nqubits) + computation_settings_3 = { + "MPI_enabled": False, + "MPS_enabled": False, + "NCCL_enabled": False, + "expectation_enabled": ham_dict, + } + backend.configure_tn_simulation(computation_settings_3) + result_tn = backend.execute_circuit(circuit=qibo_circ) + assert math.isclose(exact_expval.item(), result_tn.real.get().item(), abs_tol=1e-7)