Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 0 additions & 40 deletions .github/workflows/unit-tests.yml

This file was deleted.

21 changes: 21 additions & 0 deletions challenges/Qiskit-ibm-runtime/ibm_runtime_challenge.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# Défi - Exécuter un programme sur IBM Quantum Platform

## Objectif

L'objectif de ce défi est d'exécuter un programme quantique sur la plateforme IBM Quantum en utilisant les librairies `qiskit` et `qiskit-ibm-runtime`. Il sera demandé d'exécuter un circuit quantique préconçu sur un simulateur bruité configuré à partir d'un modèle de bruit, puis de le faire tourner sur une véritable machine.

Vous devrez comparer les résultats obtenus sur le simulateur et la machine réelle, et expliquer les différences observées.

Une étape bonus consistera à appliquer des techniques de mitigation du bruit pour améliorer les résultats obtenus sur la machine réelle, et à comparer les résultats avant et après mitigation.

## Données fournies

Un circuit quantique préconçu se trouve dans le dossier `notebooks`, accompagné d'exemples de code qui devront être complétés pour exécuter le circuit sur les différentes plateformes.

## Résultats à produire

1. (Optionel) L'histogramme des résultats obtenus à partir du simulateur idéal (sans bruit).
2. L'histogramme des résultats obtenus à partir du simulateur bruité.
3. L'histogramme des résultats obtenus à partir de la machine réelle.
4. (Optionel) Une analyse textuelle comparative des résultats obtenus avec les différentes plateformes, en mettant en évidence les différences et en formulant une hypothèse sur les causes de ces différences (par exemple, les erreurs de mesure, les erreurs de porte, etc.).
5. (Optionel) L'application de techniques de mitigation du bruit pour améliorer les résultats obtenus sur la machine réelle, et une comparaison des résultats avant et après mitigation à l'aide d'histogrammes.
10 changes: 10 additions & 0 deletions challenges/Qiskit-ibm-runtime/pyproject.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[project]
name = "ibm-runtime"
version = "0.1.0"
requires-python = ">=3.12"
dependencies = [
"matplotlib>=3.10.9",
"qiskit>=2.4.1",
"qiskit-aer>=0.17.2",
"qiskit-ibm-runtime>=0.47.0",
]
493 changes: 493 additions & 0 deletions notebooks/ibm-runtime.ipynb

Large diffs are not rendered by default.

95 changes: 95 additions & 0 deletions solutions/vqe_qchem/src/ham_sdk_conversion.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import pennylane as qml
from pennylane.ops.op_math import LinearCombination, Prod
from qiskit import QiskitError
from qiskit.quantum_info import SparsePauliOp
import pprint as pp
import logging

logger = logging.getLogger(__name__)

def map_pl_hamiltonian_to_qiskit(hamiltonian: LinearCombination) -> SparsePauliOp:
"""Convert a Pennylane Hamiltonian representation to a Qiskit representation.

The function takes in a Pennylane LinearCOmbination object and construct the corresponding Qiskit SparsePauliOp object.

the SparsePauliOp class has a method from_sparse_list that can be used to create a SparsePauliOp object from a list of (pauli_string, [indices], coeff) tuples. The pauli_string is a string representation of the Pauli operators (e.g., "XZ"), the indices are the qubits on which the operators act, and coeff is the coefficient of the term in the Hamiltonian.

Args:
hamiltonian (LinearCombination): A Pennylane Hamiltonian represented as a LinearCombination of operators.

Returns:
SparsePauliOp: A Qiskit SparsePauliOp object representing the same Hamiltonian.
"""

coeffs, pauli_ops = hamiltonian.terms()
terms = []
for pauli_op in pauli_ops:
if isinstance(pauli_op, Prod):
observables = ""
qubits = []
# Extract the individual operators and their corresponding qubits
for op in pauli_op.decomposition():
if isinstance(op, qml.X):
observables += "X"
qubits.append(op.wires[0])
elif isinstance(op, qml.Y):
observables += "Y"
qubits.append(op.wires[0])
elif isinstance(op, qml.Z):
observables += "Z"
qubits.append(op.wires[0])
elif isinstance(op, qml.Identity):
observables += "I"
qubits.append(op.wires[0])
else:
raise ValueError(f"Unsupported operator type: {type(op)}")
pauli_op = (observables, qubits)
else:
if isinstance(pauli_op, qml.X):
pauli_op = ("X", [pauli_op.wires[0]])
elif isinstance(pauli_op, qml.Y):
pauli_op = ("Y", [pauli_op.wires[0]])
elif isinstance(pauli_op, qml.Z):
pauli_op = ("Z", [pauli_op.wires[0]])
elif isinstance(pauli_op, qml.Identity):
pauli_op = ("I", [pauli_op.wires[0]])
else:
raise ValueError(f"Unsupported operator type: {type(pauli_op)}")

terms.append(pauli_op)
try:
logger.debug(f"Terms passed to SparsePauliOp...")
logger.debug(f"Terms: {pp.pformat(terms)}")
logger.debug(f"Coefficients: {pp.pformat(coeffs)}")
logger.debug(f"Number of qubits: {hamiltonian.num_wires+1}")
sparse_pauli_op = SparsePauliOp.from_sparse_list(
[
(observables, qubits, coeff)
for (observables, qubits), coeff in zip(terms, coeffs)
],
num_qubits=hamiltonian.num_wires+1,
)
except QiskitError as e:
raise ValueError(f"Error creating SparsePauliOp: {e}")

return sparse_pauli_op



if __name__ == "__main__":
hamiltonian = qml.Hamiltonian(
[0.5, 0.5, 0.5],
[
qml.X(0) @ qml.X(1),
qml.Y(0) @ qml.Y(1),
qml.Z(0) @ qml.Z(1),
]
)

pp.pp(hamiltonian.ops)

qiskit_hamiltonian = map_pl_hamiltonian_to_qiskit(hamiltonian)

pp.pp(qiskit_hamiltonian)


92 changes: 92 additions & 0 deletions solutions/vqe_qchem/src/plotting.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import json
import datetime as dt
import logging
from pathlib import Path
import matplotlib.pyplot as plt
from scipy.optimize import OptimizeResult
import numpy as np

logger = logging.getLogger(__name__)

RESULTS_DICT = {
"metadata": {
"seed": None,
"optimizer": None,
"max_iterations": None,
"qaoa_layers": None,
},
"classical": {
"known_parameters": None,
"value": None,
},
"quantum": {
"energy": None,
"optimal_parameters": None,
"hamiltonian": None,
"optimization_log": {},
},
}

def _plot_optimization_curve(
objective_log: dict[int, dict[str, float]] | list[dict[str, float]], timestamp: str
) -> None:
"""Plot the optimization history of the objective function values and saves the figure."""
if isinstance(objective_log, dict):
objective_log = [objective_log[key] for key in sorted(objective_log)]

plt.figure(figsize=(10, 6))
plt.plot(
[log["estimated_cost"] for log in objective_log],
)
plt.title(f"Optimization iterations")
plt.xlabel("Iteration")
plt.ylabel("Estimated Cost")
plt.grid()
plot_filename = Path("results") / f"optimization_history_{timestamp}.png"
plt.savefig(plot_filename, dpi=150)
logger.info(f"Optimization history plot saved to {plot_filename}")
plt.close()

def _json_default(value):
"""Convert common non-JSON types in experiment results to plain Python objects."""
if isinstance(value, complex):
return {"real": value.real, "imag": value.imag}
if isinstance(value, np.ndarray):
return value.tolist()
if isinstance(value, np.generic):
return value.item()

raise TypeError(
f"Object of type {value.__class__.__name__} is not JSON serializable"
)


def _save_results(results: dict):
"""Save the results dictionary to a JSON file with a timestamp.

Args:
results: The results dictionary to save.
"""
timestamp = dt.datetime.now().strftime("%Y%m%d_%H%M%S")
output_dir = Path("results")
output_dir.mkdir(exist_ok=True)
json_filename = f"vqe_results_{timestamp}.json"
output_file = output_dir / json_filename

with open(output_file, "w") as f:
json.dump(results, f, indent=4, default=_json_default)

_plot_optimization_curve(results["quantum"]["optimization_log"], timestamp)

def process_result(optimization_result: OptimizeResult, results: dict):
"""Process the optimization result and update the results dictionary.

Args:
optimization_result: The optimization result from scipy.optimize.minimize.
results: The dictionary to update with quantum optimization results.
"""
results["quantum"]["final_cost"] = optimization_result.fun
results["quantum"]["optimal_parameters"] = optimization_result.x.tolist()


_save_results(results)
Loading