Pennylane

The definitive open-source Python framework for quantum programming. Built by researchers, for research. Suitable for Quantum Machine Learning.

This framework can run on CPU and GPU using the built in devices. The following example shows the Pennylane framework with GPU device.

IMPORTANT: If you make use of GPU queues, it is mandatory to select a minimum number of CPU cores given by 16 cores/GPU multiplied by the amount of nodes selected and the number of selected GPUs (16 cores/gpu * num. GPUs * num. nodes).

Usage

Example script : testPennylaneGPU.sh

#!/bin/bash

#SBATCH -e pennylanegpu-errors%j.err
#SBATCH -o pennylanegpu-infos%j.msg
#SBATCH -p hopper # H100 queue
#SBATCH -N 2
#SBATCH --gres=gpu:1 # launch in 1-GPUs
#SBATCH --cpus-per-task=32 # 16 cores por GPU * 1 GPU * 2 nodos

module load nvidia-hpc-sdk/24.5
module load pennylane/0.33.1
nvcc --version
python test-pennylanegpu.py

Submit with :

sbatch --account=your_project_ID testPennylaneGPU.sh

Example script : test-pennylanegpu.py

#!/usr/bin/python

# General libraries
from scipy.ndimage import gaussian_filter
import warnings

# Pennylane libraries
import pennylane as qml
from pennylane import numpy as np

warnings.filterwarnings("ignore")

# Minimize the energy of this 4-qubit Hamiltonian given in Pauli operators
# H = 1.0 * XXII + 0.3 * ZIII + 1.0 * IXXI + 0.3 * IZII + 1.0 * IIXX + 0.3 * IIZI + 1.0 * XIIX + 0.3 * IIIZ

def ising_chain_ham(n, gam, pennylane = False):

    # This function build the hamiltonian with Pauli operators
    # n = number of spin positions
    # gam = transverse field parameter
    if pennylane:
        import pennylane as qml
        from pennylane import numpy as np

    from qiskit.opflow import X, Z, I

    for i in range(n):
        vecX = [I] * n
        vecZ = [I] * n
        vecX[i] = X
        vecZ[i] = Z

        if i == n - 1:
            vecX[0] = X
        else:
            vecX[i+1] = X

        auxX = vecX[0]
        auxZ = vecZ[0]

        for a in vecX[1:n]:
            auxX = auxX ^ a
        for b in vecZ[1:n]:
            auxZ = auxZ ^ b

        if i == 0:
            H = (auxX) + (gam * auxZ)
        else:
            H = H + (auxX) + (gam * auxZ)

    if pennylane:
        h_matrix = np.matrix(H.to_matrix_op().primitive.data)
        return qml.pauli.pauli_decompose(h_matrix)

    return H


# Hamiltonian definition
n = 4 # número de qubits
gam = 0.3

H = ising_chain_ham(n, gam, pennylane = True) # Creamos el Hamiltoniano

print("Hamiltoniano in Pauli operators:")
print("--------------------------------------------------\n")
print(H)

# choose device GPU or CPU
dev = qml.device("lightning.gpu", wires = n) # pennylane GPU simulator device
#dev = qml.device("lightning.qubit", wires = n) # pennylane CPU simulator device

init_param = (
    np.array(np.random.random(n), requires_grad=True),
    np.array(1.1, requires_grad=True),
    np.array(np.random.random(n), requires_grad=True),
)

rot_weights = np.ones(n)
crot_weights = np.ones(n)

nums_frequency = {
    "rot_param": {(0,): 1, (1,): 1, (2,): 1., (3,): 1.}, # parámetros iniciales para las rotaciones de puertas
    "layer_par": {(): n},
    "crot_param": {(0,): 2, (1,): 2, (2,): 2, (3,): 2},
}

@qml.qnode(dev)
def ansatz(rot_param, layer_par, crot_param, rot_weights = None, crot_weights = None):

    # Ansatz
    for i, par in enumerate(rot_param * rot_weights):
        qml.RY(par, wires = i)

    for _ in list(range(len(dev.wires))):

        qml.CNOT(wires = [0, 1])
        qml.CNOT(wires = [0, 2])
        qml.CNOT(wires = [1, 2])
        qml.CNOT(wires = [0, 3])
        qml.CNOT(wires = [1, 3])
        qml.CNOT(wires = [2, 3])

    # Measure of expected value for hamiltonian
    return qml.expval(H)

max_iterations = 500 # max. iterations for optimizer

# We use the Rotosolve optimizer built-in Pennylane
opt = qml.RotosolveOptimizer(substep_optimizer = "brute", substep_kwargs = {"num_steps": 4})

param = init_param

rot_weights = np.array([0.4, 0.8, 1.0, 1.2], requires_grad=False)
crot_weights = np.array([0.5, 1.0, 1.5, 1.8], requires_grad=False)

cost_rotosolve = []

for n in range(max_iterations):

    param, cost, prev_energy = opt.step_and_cost(
         ansatz,
         *param,
         nums_frequency=nums_frequency,
         spectra = [],
         full_output=True,
         rot_weights=rot_weights,
         crot_weights=crot_weights,
    )

    # Compute energy
    energy = ansatz(*param, rot_weights=rot_weights, crot_weights=crot_weights)

    # Calculate difference between new and old energies
    conv = np.abs(energy - prev_energy)

    if n % 10 == 0:
        print("Iteration = {:},  Energy = {:.15f} Ha,  Convergence parameter = {:.15f} Ha".format(n, energy, np.mean(conv)))

    cost_rotosolve.extend(prev_energy)

print("\n==================================")
print("Number of iterations = ", max_iterations)
print("Last energy value = ", cost_rotosolve[len(cost_rotosolve) - 1])

qml.about()

More info :