Beispiel #1
0
def assert_gates_equivalent(qibo_gate,
                            cirq_gates,
                            nqubits,
                            ndevices=None,
                            atol=1e-7):
    """Asserts that QIBO and Cirq gates have equivalent action on a random state.

    Args:
        qibo_gate: QIBO gate.
        cirq_gates: List of tuples (cirq gate, target qubit IDs).
        nqubits: Total number of qubits in the circuit.
        atol: Absolute tolerance in state vector comparsion.
    """
    initial_state = random_state(nqubits)
    target_state, target_depth = execute_cirq(cirq_gates, nqubits,
                                              np.copy(initial_state))
    accelerators = None
    if ndevices is not None:
        accelerators = {"/GPU:0": ndevices}

    if accelerators:
        if not K.supports_multigpu:
            with pytest.raises(NotImplementedError):
                c = models.Circuit(nqubits, accelerators)
        elif K.get_platform() == "numba" and len(
                K.available_platforms) > 1:  # pragma: no cover
            pytest.skip("Skipping distributed cirq test for numba platform.")
    else:
        c = models.Circuit(nqubits, accelerators)
        c.add(qibo_gate)
        final_state = c(np.copy(initial_state))
        assert c.depth == target_depth
        K.assert_allclose(final_state, target_state, atol=atol)
def test_circuit_with_noise_noise_map():
    """Check ``circuit.with_noise() when giving noise map."""
    original_backend = qibo.get_backend()
    qibo.set_backend("matmuleinsum")
    noise_map = {0: (0.1, 0.2, 0.1), 1: (0.2, 0.3, 0.0),
                 2: (0.0, 0.0, 0.0)}

    c = models.Circuit(3)
    c.add([gates.H(0), gates.H(1), gates.X(2)])
    c.add(gates.M(2))
    noisy_c = c.with_noise(noise_map, measurement_noise = (0.3, 0.0, 0.0))

    target_c = models.Circuit(3)
    target_c.add(gates.H(0))
    target_c.add(gates.NoiseChannel(0, 0.1, 0.2, 0.1))
    target_c.add(gates.NoiseChannel(1, 0.2, 0.3, 0.0))
    target_c.add(gates.H(1))
    target_c.add(gates.NoiseChannel(0, 0.1, 0.2, 0.1))
    target_c.add(gates.NoiseChannel(1, 0.2, 0.3, 0.0))
    target_c.add(gates.X(2))
    target_c.add(gates.NoiseChannel(0, 0.1, 0.2, 0.1))
    target_c.add(gates.NoiseChannel(1, 0.2, 0.3, 0.0))
    target_c.add(gates.NoiseChannel(2, 0.3, 0.0, 0.0))

    final_state = noisy_c().numpy()
    target_state = target_c().numpy()
    np.testing.assert_allclose(target_state, final_state)
    qibo.set_backend(original_backend)
Beispiel #3
0
def test_measurements_with_probabilistic_noise():
    """Check measurements when simulating noise with repeated execution."""
    import tensorflow as tf
    thetas = np.random.random(5)
    c = models.Circuit(5)
    c.add((gates.RX(i, t) for i, t in enumerate(thetas)))
    c.add((gates.PauliNoiseChannel(i, px=0.0, py=0.2, pz=0.4, seed=123)
           for i in range(5)))
    c.add(gates.M(*range(5)))
    tf.random.set_seed(123)
    result = c(nshots=20)

    np.random.seed(123)
    tf.random.set_seed(123)
    target_samples = []
    for _ in range(20):
        noiseless_c = models.Circuit(5)
        noiseless_c.add((gates.RX(i, t) for i, t in enumerate(thetas)))
        for i in range(5):
            if np.random.random() < 0.2:
                noiseless_c.add(gates.Y(i))
            if np.random.random() < 0.4:
                noiseless_c.add(gates.Z(i))
        noiseless_c.add(gates.M(*range(5)))
        target_samples.append(noiseless_c(nshots=1).samples())
    target_samples = tf.concat(target_samples, axis=0)
    np.testing.assert_allclose(result.samples(), target_samples)
Beispiel #4
0
def assert_gates_equivalent(qibo_gate, cirq_gates, nqubits,
                            ndevices=None, atol=1e-7):
    """Asserts that QIBO and Cirq gates have equivalent action on a random state.

    Args:
        qibo_gate: QIBO gate.
        cirq_gates: List of tuples (cirq gate, target qubit IDs).
        nqubits: Total number of qubits in the circuit.
        atol: Absolute tolerance in state vector comparsion.
    """
    initial_state = random_state(nqubits)
    target_state, target_depth = execute_cirq(cirq_gates, nqubits,
                                              np.copy(initial_state))
    backend = qibo.get_backend()
    if ndevices is None or "numpy" in backend:
        accelerators = None
    else:
        accelerators = {"/GPU:0": ndevices}

    if backend != "custom" and accelerators:
        with pytest.raises(NotImplementedError):
            c = models.Circuit(nqubits, accelerators)
            c.add(qibo_gate)
    else:
        c = models.Circuit(nqubits, accelerators)
        c.add(qibo_gate)
        final_state = c(np.copy(initial_state))
        assert c.depth == target_depth
        np.testing.assert_allclose(final_state, target_state, atol=atol)
def test_measurement_qubit_order_simple():
    """Check that measurement results follow order defined by user."""
    c = models.Circuit(2)
    c.add(gates.X(0))
    c.add(gates.M(1, 0))
    result1 = c(nshots=100)

    c = models.Circuit(2)
    c.add(gates.X(0))
    c.add(gates.M(1))
    c.add(gates.M(0))
    result2 = c(nshots=100)

    target_binary_samples = np.zeros((100, 2))
    target_binary_samples[:, 1] = 1
    target = {
        "decimal_samples": np.ones((100, )),
        "binary_samples": target_binary_samples,
        "decimal_frequencies": {
            1: 100
        },
        "binary_frequencies": {
            "01": 100
        }
    }
    assert_results(result1, **target)
    assert_results(result2, **target)
def test_measurements_with_probabilistic_noise(backend):
    """Check measurements when simulating noise with repeated execution."""
    original_backend = qibo.get_backend()
    qibo.set_backend(backend)
    thetas = np.random.random(5)
    c = models.Circuit(5)
    c.add((gates.RX(i, t) for i, t in enumerate(thetas)))
    c.add((gates.PauliNoiseChannel(i, px=0.0, py=0.2, pz=0.4, seed=123)
           for i in range(5)))
    c.add(gates.M(*range(5)))
    K.set_seed(123)
    samples = c(nshots=20).samples()

    np.random.seed(123)
    K.set_seed(123)
    target_samples = []
    for _ in range(20):
        noiseless_c = models.Circuit(5)
        noiseless_c.add((gates.RX(i, t) for i, t in enumerate(thetas)))
        for i in range(5):
            if np.random.random() < 0.2:
                noiseless_c.add(gates.Y(i))
            if np.random.random() < 0.4:
                noiseless_c.add(gates.Z(i))
        noiseless_c.add(gates.M(*range(5)))
        target_samples.append(noiseless_c(nshots=1).samples())
    target_samples = np.concatenate(target_samples, axis=0)
    np.testing.assert_allclose(samples, target_samples)
    qibo.set_backend(original_backend)
Beispiel #7
0
def test_circuit_constructor_hardware_errors():
    K.is_hardware = True
    with pytest.raises(NotImplementedError):
        c = models.Circuit(5, accelerators={"/GPU:0": 2})
    with pytest.raises(NotImplementedError):
        c = models.Circuit(5, density_matrix=True)
    K.is_hardware = False
Beispiel #8
0
def assert_gates_equivalent(qibo_gate,
                            cirq_gates,
                            nqubits,
                            ndevices=None,
                            atol=1e-7):
    """Asserts that QIBO and Cirq gates have equivalent action on a random state.

    Args:
        qibo_gate: QIBO gate.
        cirq_gates: List of tuples (cirq gate, target qubit IDs).
        nqubits: Total number of qubits in the circuit.
        atol: Absolute tolerance in state vector comparsion.
    """
    initial_state = utils.random_numpy_state(nqubits)
    target_state = execute_cirq(cirq_gates, nqubits, np.copy(initial_state))
    accelerators = None if ndevices is None else {"/GPU:0": ndevices}

    if isinstance(qibo_gate, native_gates.TensorflowGate) and accelerators:
        with pytest.raises(NotImplementedError):
            c = models.Circuit(nqubits, accelerators)
            c.add(qibo_gate)
    else:
        c = models.Circuit(nqubits, accelerators)
        c.add(qibo_gate)
        final_state = c(np.copy(initial_state)).numpy()
        np.testing.assert_allclose(target_state, final_state, atol=atol)
Beispiel #9
0
def test_density_matrix_circuit_errors():
    """Check errors of circuits that simulate density matrices."""
    # Attempt to distribute density matrix circuit
    with pytest.raises(NotImplementedError):
        c = models.Circuit(5, accelerators={"/GPU:0": 2}, density_matrix=True)
    # Attempt to add Kraus channel to non-density matrix circuit
    c = models.Circuit(5)
    with pytest.raises(ValueError):
        c.add(gates.KrausChannel([((0, ), np.eye(2))]))
Beispiel #10
0
def test_circuit_addition_with_measurements(backend):
    c = models.Circuit(2)
    c.add(gates.H(0))
    c.add(gates.H(1))

    meas_c = models.Circuit(2)
    c.add(gates.M(0, 1))

    c += meas_c
    assert len(c.measurement_gate.target_qubits) == 2
    assert c.measurement_tuples == {"register0": (0, 1)}
def test_registers_with_same_name_error():
    """Check that registers with the same name cannot be added."""
    c1 = models.Circuit(2)
    c1.add(gates.H(0))
    c1.add(gates.M(0))

    c2 = models.Circuit(2)
    c2.add(gates.H(1))
    c2.add(gates.M(1))

    with pytest.raises(KeyError):
        c = c1 + c2
def test_circuit_addition_with_measurements():
    """Check if measurements are transferred during circuit addition."""
    c = models.Circuit(2)
    c.add(gates.H(0))
    c.add(gates.H(1))

    meas_c = models.Circuit(2)
    c.add(gates.M(0, 1))

    c += meas_c
    assert len(c.measurement_gate.target_qubits) == 2
    assert c.measurement_tuples == {"register0": (0, 1)}
def test_measurement_result_parameters(backend, accelerators, effect):
    c = models.Circuit(4, accelerators)
    if effect:
        c.add(gates.X(0))
    output = c.add(gates.M(0, collapse=True))
    c.add(gates.RX(1, theta=np.pi * output / 4))

    target_c = models.Circuit(4)
    if effect:
        target_c.add(gates.X(0))
        target_c.add(gates.RX(1, theta=np.pi / 4))
    K.assert_allclose(c(), target_c())
Beispiel #14
0
def test_circuit_addition_with_measurements_in_both_circuits(backend, accelerators):
    c1 = models.Circuit(4, accelerators)
    c1.add(gates.H(0))
    c1.add(gates.H(1))
    c1.add(gates.M(1, register_name="a"))

    c2 = models.Circuit(4, accelerators)
    c2.add(gates.X(0))
    c2.add(gates.M(0, register_name="b"))

    c = c1 + c2
    assert len(c.measurement_gate.target_qubits) == 2
    assert c.measurement_tuples == {"a": (1,), "b": (0,)}
Beispiel #15
0
def test_unsupported_gates_errors():
    c = models.Circuit(4, {"/GPU:0": 2})
    c.add(gates.H(0))
    c.add(gates.H(1))
    c.queues.qubits = distutils.DistributedQubits([0], c.nqubits)
    with pytest.raises(ValueError):
        c.queues.create(c.queue)

    c = models.Circuit(4, {"/GPU:0": 4})
    c.add(gates.SWAP(0, 1))
    c.queues.qubits = distutils.DistributedQubits([0, 1], c.nqubits)
    with pytest.raises(ValueError):
        c.queues.create(c.queue)
def test_circuit_addition_with_measurements_in_both_circuits(accelerators):
    """Check if measurements of two circuits are added during circuit addition."""
    c1 = models.Circuit(2, accelerators)
    c1.add(gates.H(0))
    c1.add(gates.H(1))
    c1.add(gates.M(1, register_name="a"))

    c2 = models.Circuit(2, accelerators)
    c2.add(gates.X(0))
    c2.add(gates.M(0, register_name="b"))

    c = c1 + c2
    assert len(c.measurement_gate.target_qubits) == 2
    assert c.measurement_tuples == {"a": (1, ), "b": (0, )}
Beispiel #17
0
def test_density_matrix_circuit_errors():
    """Check errors of circuits that simulate density matrices."""
    # Switch `gate.density_matrix` to `True` after setting `nqubits`
    gate = gates.X(0)
    gate.nqubits = 2
    with pytest.raises(RuntimeError):
        gate.density_matrix = True
    # Attempt to distribute density matrix circuit
    with pytest.raises(NotImplementedError):
        c = models.Circuit(5, accelerators={"/GPU:0": 2}, density_matrix=True)
    # Attempt to add Kraus channel to non-density matrix circuit
    c = models.Circuit(5)
    with pytest.raises(ValueError):
        c.add(gates.KrausChannel([((0, ), np.eye(2))]))
Beispiel #18
0
def test_registers_with_same_name_error(backend):
    """Check that circuits that contain registers with the same name cannot be added."""
    original_backend = qibo.get_backend()
    qibo.set_backend(backend)
    c1 = models.Circuit(2)
    c1.add(gates.H(0))
    c1.add(gates.M(0))

    c2 = models.Circuit(2)
    c2.add(gates.H(1))
    c2.add(gates.M(1))

    with pytest.raises(KeyError):
        c = c1 + c2
    qibo.set_backend(original_backend)
Beispiel #19
0
def test_gate_after_measurement_with_addition_error(backend, accelerators):
    c = models.Circuit(4, accelerators)
    c.add(gates.H(0))
    c.add(gates.M(1))

    # Try to add gate to qubit that is already measured
    c2 = models.Circuit(4, accelerators)
    c2.add(gates.H(1))
    with pytest.raises(ValueError):
        c += c2
    # Try to add measurement to qubit that is already measured
    c2 = models.Circuit(4, accelerators)
    c2.add(gates.M(1, register_name="a"))
    with pytest.raises(ValueError):
        c += c2
Beispiel #20
0
def test_final_state(backend, accelerators):
    """Check that final state is logged correctly when using measurements."""
    c = models.Circuit(4, accelerators)
    c.add(gates.X(1))
    c.add(gates.X(2))
    c.add(gates.M(0, 1))
    c.add(gates.M(2))
    c.add(gates.X(3))
    result = c(nshots=100)
    c = models.Circuit(4, accelerators)
    c.add(gates.X(1))
    c.add(gates.X(2))
    c.add(gates.X(3))
    target_state = c()
    K.assert_allclose(c.final_state, target_state)
Beispiel #21
0
def test_circuit_constructor():
    from qibo.core.circuit import Circuit, DensityMatrixCircuit
    from qibo.core.distcircuit import DistributedCircuit
    c = models.Circuit(5)
    assert isinstance(c, Circuit)
    c = models.Circuit(5, density_matrix=True)
    assert isinstance(c, DensityMatrixCircuit)
    if not K.supports_multigpu:  # pragma: no cover
        with pytest.raises(NotImplementedError):
            c = models.Circuit(5, accelerators={"/GPU:0": 2})
    else:
        c = models.Circuit(5, accelerators={"/GPU:0": 2})
        assert isinstance(c, DistributedCircuit)
    with pytest.raises(NotImplementedError):
        c = models.Circuit(5, accelerators={"/GPU:0": 2}, density_matrix=True)
Beispiel #22
0
def test_collapse_gate(backend, nqubits, targets, results):
    original_backend = qibo.get_backend()
    qibo.set_backend(backend)
    initial_psi = utils.random_numpy_state(nqubits)
    initial_rho = np.outer(initial_psi, initial_psi.conj())
    c = models.Circuit(nqubits, density_matrix=True)
    c.add(gates.Collapse(*targets, result=results))
    final_rho = c(np.copy(initial_rho))

    c = models.Circuit(nqubits)
    c.add(gates.Collapse(*targets, result=results))
    target_psi = c(np.copy(initial_psi)).numpy()
    target_rho = np.outer(target_psi, target_psi.conj())
    np.testing.assert_allclose(final_rho, target_rho)
    qibo.set_backend(original_backend)
def ansatz_Fourier_2D(layers, qubits=1):
    """
    3 parameters per layer: Ry(wx + a), Rz(b)
    """
    circuit = models.Circuit(qubits)
    for _ in range(layers):
        circuit.add(gates.RY(0, theta=0))
        circuit.add(gates.RZ(0, theta=0))
        circuit.add(gates.RY(0, theta=0))
        circuit.add(gates.RZ(0, theta=0))

    def rotation(theta, x):
        p = circuit.get_parameters()
        i = 0
        j = 0
        for l in range(layers):
            p[i] = theta[j]
            p[i + 1] = theta[j + 1] + theta[j + 2:j + 4] @ x
            p[i + 2] = theta[j + 4]
            p[i + 3] = theta[j + 5]
            i += 4
            j += 6

        return p

    nparams = 6 * (layers)
    return circuit, rotation, nparams
def test_unbalanced_probabilistic_measurement(backend, use_samples):
    original_backend = qibo.get_backend()
    original_threads = qibo.get_threads()
    qibo.set_backend(backend)
    # set single-thread to fix the random values generated from the frequency custom op
    qibo.set_threads(1)
    state = np.array([1, 1, 1, np.sqrt(3)]) / np.sqrt(6)
    c = models.Circuit(2)
    c.add(gates.Flatten(state))
    c.add(gates.M(0, 1))
    result = c(nshots=1000)

    K.set_seed(1234)
    if use_samples:
        # calculates sample tensor directly using `tf.random.categorical`
        # otherwise it uses the frequency-only calculation
        _ = result.samples()
    # update reference values based on backend and device
    if K.name == "tensorflow":
        if K.gpu_devices:  # pragma: no cover
            # CI does not use GPU
            decimal_frequencies = {0: 196, 1: 153, 2: 156, 3: 495}
        else:
            decimal_frequencies = {0: 168, 1: 188, 2: 154, 3: 490}
    elif K.name == "numpy":
        decimal_frequencies = {0: 171, 1: 148, 2: 161, 3: 520}
    assert sum(result.frequencies().values()) == 1000
    assert_result(result, decimal_frequencies=decimal_frequencies)
    qibo.set_backend(original_backend)
    qibo.set_threads(original_threads)
Beispiel #25
0
def test_circuit_dm(backend):
    """Check passing density matrix as initial state to a circuit."""
    original_backend = qibo.get_backend()
    qibo.set_backend(backend)
    theta = 0.1234
    initial_rho = utils.random_density_matrix(3)

    c = models.Circuit(3, density_matrix=True)
    c.add(gates.H(0))
    c.add(gates.H(1))
    c.add(gates.CNOT(0, 1))
    c.add(gates.H(2))
    final_rho = c(np.copy(initial_rho))

    h = np.array([[1, 1], [1, -1]]) / np.sqrt(2)
    cnot = np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]])
    m1 = np.kron(np.kron(h, h), np.eye(2))
    m2 = np.kron(cnot, np.eye(2))
    m3 = np.kron(np.eye(4), h)
    target_rho = m1.dot(initial_rho).dot(m1.T.conj())
    target_rho = m2.dot(target_rho).dot(m2.T.conj())
    target_rho = m3.dot(target_rho).dot(m3.T.conj())

    np.testing.assert_allclose(final_rho, target_rho)
    qibo.set_backend(original_backend)
def test_post_measurement_bitflips_on_circuit_result(backend):
    """Check bitflip errors on ``CircuitResult`` objects."""
    original_backend = qibo.get_backend()
    qibo.set_backend(backend)
    thetas = np.random.random(4)
    c = models.Circuit(4)
    c.add((gates.RX(i, theta=t) for i, t in enumerate(thetas)))
    c.add(gates.M(0, 1, register_name="a"))
    c.add(gates.M(3, register_name="b"))
    result = c(nshots=30)
    samples = result.samples()
    K.set_seed(123)
    noisy_result = result.copy().apply_bitflips({0: 0.2, 1: 0.4, 3: 0.3})
    noisy_samples = noisy_result.samples(binary=True)
    register_samples = noisy_result.samples(binary=True, registers=True)

    K.set_seed(123)
    sprobs = np.array(K.random_uniform(samples.shape))
    flipper = sprobs < np.array([0.2, 0.4, 0.3])
    target_samples = (samples + flipper) % 2
    np.testing.assert_allclose(noisy_samples, target_samples)
    # Check register samples
    np.testing.assert_allclose(register_samples["a"], target_samples[:, :2])
    np.testing.assert_allclose(register_samples["b"], target_samples[:, 2:])
    qibo.set_backend(original_backend)
def test_probabilistic_measurement(backend, accelerators, use_samples):
    original_backend = qibo.get_backend()
    original_threads = qibo.get_threads()
    qibo.set_backend(backend)
    # set single-thread to fix the random values generated from the frequency custom op
    qibo.set_threads(1)
    c = models.Circuit(4, accelerators)
    c.add(gates.H(0))
    c.add(gates.H(1))
    c.add(gates.M(0, 1))
    result = c(nshots=1000)

    K.set_seed(1234)
    if use_samples:
        # calculates sample tensor directly using `tf.random.categorical`
        # otherwise it uses the frequency-only calculation
        _ = result.samples()

    # update reference values based on backend and device
    if K.name == "tensorflow":
        if K.gpu_devices:  # pragma: no cover
            # CI does not use GPU
            decimal_frequencies = {0: 273, 1: 233, 2: 242, 3: 252}
        else:
            decimal_frequencies = {0: 271, 1: 239, 2: 242, 3: 248}
    elif K.name == "numpy":
        decimal_frequencies = {0: 249, 1: 231, 2: 253, 3: 267}
    assert sum(result.frequencies().values()) == 1000
    assert_result(result, decimal_frequencies=decimal_frequencies)
    qibo.set_backend(original_backend)
    qibo.set_threads(original_threads)
Beispiel #28
0
def main(nqubits, layers, maxsteps, T_max):
    circuit = models.Circuit(nqubits)
    for l in range(layers):
        circuit.add((gates.RY(q, theta=0) for q in range(nqubits)))
        circuit.add((gates.CZ(q, q + 1) for q in range(0, nqubits - 1, 2)))
        circuit.add((gates.RY(q, theta=0) for q in range(nqubits)))
        circuit.add((gates.CZ(q, q + 1) for q in range(1, nqubits - 2, 2)))
        circuit.add(gates.CZ(0, nqubits - 1))
    circuit.add((gates.RY(q, theta=0) for q in range(nqubits)))
    problem_hamiltonian = hamiltonians.XXZ(nqubits)
    easy_hamiltonian = hamiltonians.X(nqubits)
    s = lambda t: t
    aavqe = models.variational.AAVQE(circuit, easy_hamiltonian, problem_hamiltonian, s, nsteps=maxsteps, t_max=T_max)

    initial_parameters = np.random.uniform(0, 2 * np.pi*0.1,
                                           2 * nqubits * layers + nqubits)
    best, params = aavqe.minimize(initial_parameters)

    print('Final parameters: ', params)
    print('Final energy: ', best)

    #We compute the difference from the exact value to check performance
    eigenvalue = problem_hamiltonian.eigenvalues()
    print('Difference from exact value: ',best - K.real(eigenvalue[0]))
    print('Log difference: ',-np.log10(best - K.real(eigenvalue[0])))
def test_vqe_custom_gates_errors():
    """Check that ``RuntimeError``s is raised when using custom gates."""
    if "qibotf" not in qibo.K.available_backends:  # pragma: no cover
        pytest.skip("Custom backend not available.")

    original_backend = qibo.get_backend()
    qibo.set_backend("qibotf")

    nqubits = 6
    circuit = models.Circuit(nqubits)
    for q in range(nqubits):
        circuit.add(gates.RY(q, theta=0))
    for q in range(0, nqubits - 1, 2):
        circuit.add(gates.CZ(q, q + 1))

    hamiltonian = hamiltonians.XXZ(nqubits=nqubits)
    initial_parameters = np.random.uniform(0, 2 * np.pi, 2 * nqubits + nqubits)
    v = models.VQE(circuit, hamiltonian)
    # compile with custom gates
    with pytest.raises(RuntimeError):
        best, params, _ = v.minimize(initial_parameters,
                                     method="BFGS",
                                     options={'maxiter': 1},
                                     compile=True)
    # use SGD with custom gates
    with pytest.raises(RuntimeError):
        best, params, _ = v.minimize(initial_parameters,
                                     method="sgd",
                                     compile=False)
    qibo.set_backend(original_backend)
Beispiel #30
0
def test_distributed_circuit_addition():
    # Attempt to add circuits with different devices
    original_backend = qibo.get_backend()
    qibo.set_backend("custom")
    devices = {"/GPU:0": 2, "/GPU:1": 2}
    c1 = models.DistributedCircuit(6, devices)
    c2 = models.DistributedCircuit(6, {"/GPU:0": 2})
    with pytest.raises(ValueError):
        c = c1 + c2

    c2 = models.DistributedCircuit(6, devices)
    c1.add([gates.H(i) for i in range(6)])
    c2.add([gates.CNOT(i, i + 1) for i in range(5)])
    c2.add([gates.Z(i) for i in range(6)])
    dist_c = c1 + c2

    c = models.Circuit(6)
    c.add([gates.H(i) for i in range(6)])
    c.add([gates.CNOT(i, i + 1) for i in range(5)])
    c.add([gates.Z(i) for i in range(6)])

    target_state = c().numpy()
    final_state = dist_c().numpy()
    assert c.depth == dist_c.depth
    np.testing.assert_allclose(target_state, final_state)
    qibo.set_backend(original_backend)