Skip to navigationSkip to main contentSkip to footerScaleway Docs HomepageAsk our AI
Ask our AI

Use Qiskit ML plugin for Quantum Machine Learning

Qiskit is a comprehensive, open-source SDK for working with quantum computers. Its dedicated plugin, qiskit-machine-learning, introduces foundational quantum machine learning (QML) algorithms, neural networks, and tools to integrate quantum circuits seamlessly into classic machine learning workflows.

Scaleway offers access to the qiskit-scaleway provider, enabling you to run your Qiskit circuits and QML models directly on Scaleway's QaaS infrastructure with minimal configuration.

In this practical example, we will build a Quantum Autoencoder using qiskit-machine-learning to compress and reconstruct a custom toy dataset of digital numbers (zeros and ones) on Scaleway's infrastructure. This example is freely adapted from Qiskit's Quantum Autoencoder tutorial.

How to perform Quantum Neural Network training on Scaleway using Qiskit-ML

Before you start

To complete the actions presented below, you must have:

  • A Scaleway account with a valid Project ID
  • A Scaleway API key (Secret Key)
  • Python installed on your local machine
  1. Install the qiskit-scaleway provider alongside the necessary machine learning and visualization packages:

    pip install qiskit-machine-learning qiskit-scaleway pylatexenc matplotlib
  2. Create a helper file named digits_dataset.py in your working directory, and add the helper script shown below. This script generates a basic 5x3 grid representing handwritten "zeros" and "ones" that we will use to train our model.

    import matplotlib.pyplot as plt
    import numpy as np
    from qiskit_machine_learning.utils import algorithm_globals
    
    algorithm_globals.random_seed = 42
    
    def zero_idx():
        # Index for zero pixels (Perimeter of a 5x3 grid)
        return [[0,1], [1,0], [1,2], [2,0], [2,2], [3,0], [3,2], [4,1]]
    
    def one_idx():
        # Index for one pixels (Middle column of a 5x3 grid)
        return [[0,1], [1,0], [1,1], [2,1], [3,1], [4,1]]
    
    def get_dataset_digits(num, draw=True):
        train_images = []
        train_labels = []
    
        for i in range(int(num / 2)):
            empty = np.array([algorithm_globals.random.uniform(0, 0.1) for _ in range(15)]).reshape(5, 3)
            for r, c in one_idx():
                empty[r][c] = algorithm_globals.random.uniform(0.9, 1)
            train_images.append(empty)
            train_labels.append(1)
            if draw:
                plt.title("This is a One")
                plt.imshow(train_images[-1], vmin=0, vmax=1)
                plt.show(block=True)
    
        for i in range(int(num / 2)):
            empty = np.array([algorithm_globals.random.uniform(0, 0.1) for _ in range(15)]).reshape(5, 3)
            for r, c in zero_idx():
                empty[r][c] = algorithm_globals.random.uniform(0.9, 1)
            train_images.append(empty)
            train_labels.append(0)
            if draw:
                plt.imshow(train_images[-1], vmin=0, vmax=1)
                plt.title("This is a Zero")
                plt.show(block=True)
    
        train_images = np.array(train_images)
        # Flatten to 15-dimensional vectors for the 15 data qubits
        train_images = train_images.reshape(len(train_images), 15)
    
        return train_images, train_labels
  3. Create the main training script. Save the following code as qml_tutorial.py. Make sure to define your SCW_PROJECT_ID and SCW_SECRET_KEY as environment variables (or via an .env file) before running it. Notice how the ScalewayProvider is used to fetch the EMU-AER-2L4 backend and initiate a dedicated session for the iterative COBYLA optimizer.

    import os
    import time
    
    import matplotlib.pyplot as plt
    import numpy as np
    
    from qiskit import ClassicalRegister, QuantumRegister, QuantumCircuit
    from qiskit.circuit import ParameterVector
    from qiskit.circuit.library import real_amplitudes
    from qiskit.quantum_info import Statevector
    
    from qiskit_scaleway import ScalewayProvider
    from qiskit_scaleway.primitives import Sampler as ScwSampler
    
    from qiskit_machine_learning.optimizers import COBYLA
    from qiskit_machine_learning.utils import algorithm_globals
    from qiskit_machine_learning.neural_networks import SamplerQNN
    
    from digits_dataset import get_dataset_digits
    
    algorithm_globals.random_seed = 42
    
    ### Instantiate Scaleway provider ###
    PROJECT_ID = os.getenv("SCW_PROJECT_ID")
    SECRET_KEY = os.getenv("SCW_SECRET_KEY")
    provider = ScalewayProvider(
        project_id=PROJECT_ID,
        secret_key=SECRET_KEY,
    )
    backend = provider.get_backend("EMU-AER-2L4")
    session_id = backend.start_session()
    sampler = ScwSampler(backend, session_id, options={"default_shots": 100})
    print(sampler)
    
    ### Helpers ###
    def ansatz(num_qubits: int):
        return real_amplitudes(num_qubits, reps=3)
    
    def encoder_circuit(num_latent: int, num_trash: int) -> QuantumCircuit:
        qr = QuantumRegister(num_latent + 2 * num_trash + 1, "q")
        cr = ClassicalRegister(1, "c")
        circuit = QuantumCircuit(qr, cr)
        circuit.compose(ansatz(num_latent + num_trash), range(0, num_latent + num_trash), inplace=True)
        circuit.barrier()
        auxiliary_qubit = num_latent + 2 * num_trash
    
        # swap test
        circuit.h(auxiliary_qubit)
        for i in range(num_trash):
            circuit.cswap(auxiliary_qubit, num_latent + i, num_latent + num_trash + i)
    
        circuit.h(auxiliary_qubit)
        circuit.measure(auxiliary_qubit, cr[0])
        return circuit
    
    fig, ax, line = None, None, None
    
    def cost_func_digits(params_values) -> float:
        global fig, ax, line 
    
        probabilities = qnn.forward(train_images, params_values)
        cost = np.sum(probabilities[:, 1]) / train_images.shape[0]
    
        if not objective_func_vals:
            plt.ion() 
            fig, ax = plt.subplots()
            ax.set_title("Objective function value against iteration")
            ax.set_xlabel("Iteration")
            ax.set_ylabel("Objective function value")
            line, = ax.plot([], [], color='blue', marker='o', markersize=4)
            plt.show(block=False)
    
        objective_func_vals.append(cost)
        line.set_data(range(len(objective_func_vals)), objective_func_vals)
        ax.relim()
        ax.autoscale_view()
        fig.canvas.draw()
        fig.canvas.flush_events()
    
        return cost
    
    ### MAIN SCRIPT ###
    num_latent = 9
    num_trash = 6
    
    train_images, __ =  get_dataset_digits(4, draw=False)
    
    # Custom Unitary Feature Map (Avoids OpenQASM 3 Serialization errors)
    num_features = num_latent + num_trash
    feature_map = QuantumCircuit(num_features)
    inputs = ParameterVector("x", num_features)
    for i in range(num_features):
        # Mapping [0, 1] data into RY rotation probability. 
        feature_map.ry(np.pi * inputs[i], i)
    
    ae = encoder_circuit(num_latent, num_trash)
    
    qc = QuantumCircuit(num_latent + 2 * num_trash + 1, 1)
    qc = qc.compose(feature_map, range(num_latent + num_trash))
    qc = qc.compose(ae)
    
    qc.draw(output="mpl", style="clifford")
    plt.show(block=True)
    
    qnn = SamplerQNN(
        circuit=qc,
        input_params=feature_map.parameters,
        weight_params=ae.parameters,
        interpret=lambda x: x,
        output_shape=2,
        sampler=sampler,
    )
    
    opt = COBYLA(maxiter=150)
    initial_point = algorithm_globals.random.random(ae.num_parameters)
    objective_func_vals = []
    
    plt.rcParams["figure.figsize"] = (12, 6)
    
    start = time.time()
    opt_result = opt.minimize(fun=cost_func_digits, x0=initial_point)
    elapsed = time.time() - start
    print(f"Fit in {elapsed:0.2f} seconds")
    plt.ioff()
    plt.show(block=True)
    
    # Test our circuit
    test_qc = QuantumCircuit(num_latent + num_trash)
    test_qc = test_qc.compose(feature_map)
    ansatz_qc = ansatz(num_latent + num_trash)
    test_qc = test_qc.compose(ansatz_qc)
    test_qc.barrier()
    
    # Dynamically reset the designated trash qubits
    for i in range(num_latent, num_latent + num_trash):
        test_qc.reset(i)
    
    test_qc.barrier()
    test_qc = test_qc.compose(ansatz_qc.inverse())
    
    # Helper to visually decode our RY probabilities back into images
    def extract_image(state_circ):
        sv = Statevector(state_circ)
        pixels = np.zeros(15)
        for i in range(15):
            # Marginal probability of measuring |1> on qubit i
            pixels[i] = sv.probabilities([i])[1]
        return pixels.reshape(5, 3)
    
    # Sample new images and reconstruct
    test_images, test_labels = get_dataset_digits(2, draw=False)
    for image, label in zip(test_images, test_labels):
        original_qc = feature_map.assign_parameters(image)
        original_img = extract_image(original_qc)
    
        param_values = np.concatenate((image, opt_result.x))
        output_qc = test_qc.assign_parameters(param_values)
        output_img = extract_image(output_qc)
    
        fig, (ax1, ax2) = plt.subplots(1, 2)
        ax1.imshow(original_img, vmin=0, vmax=1)
        ax1.set_title("Input Data")
        ax2.imshow(output_img, vmin=0, vmax=1)
        ax2.set_title("Output Data")
        plt.show(block=True)
  4. Run the script.

    python ~/qml_tutorial.py

Executing this code will iteratively train the parameters of the quantum autoencoder, visually plotting the decreasing cost function in real-time. Once convergence is reached, the script will output a comparison of the newly reconstructed digits against the original input data for comparison.

Understanding session management

Because quantum machine learning algorithms often require hundreds of iterative steps (like the COBYLA optimization in the script above), submitting each circuit independently can introduce heavy network overhead. With qiskit-scaleway, your iterative workloads are grouped via a session. By explicitly calling start_session() on your backend and passing that session_id to your ScwSampler primitive, you guarantee that Scaleway's QaaS infrastructure processes your training loop in the same session.

Tip

Refer to the Quantum Computing information page for more details on how emulation platforms and billing work.

No Results