Skip to main content
IBM Quantum Platform

Sérialisation QPY

qiskit.qpy

QPY est un format de sérialisation binaire pour les objets QuantumCircuit qui est conçu pour être multiplateforme, Python version agnostique, et rétrocompatible à l'avenir. QPY doit être utilisé si vous avez besoin d'un mécanisme pour sauvegarder ou copier entre les systèmes un objet Qiskit QuantumCircuit qui préserve la structure complète de l'objet Qiskit (à l'exception des attributs personnalisés définis en dehors du code Qiskit). Cela diffère d'autres formats de sérialisation tels que OpenQASM ( 2.0 ou 3.0 ) qui a un modèle d'abstraction différent et peut entraîner une perte d'informations contenues dans le circuit original (ou est incapable de représenter certains aspects des objets Qiskit) ou Python 's pickle qui préservera exactement l'objet Qiskit mais ne fonctionnera que pour une seule version de Qiskit (il est également potentiellement non sécurisé ).


Utilisation de base

L'utilisation de QPY est définie pour être simple et refléter l'API utilisateur des sérialiseurs de la bibliothèque standard de Python, pickle et json. Il y a 2 fonctions orientées vers l'utilisateur : qiskit.qpy.dump() et qiskit.qpy.load() qui sont utilisées pour décharger des données QPY dans un objet fichier et charger des circuits à partir de données QPY dans un objet fichier, respectivement. Par exemple :

from qiskit.circuit import QuantumCircuit
from qiskit import qpy

qc = QuantumCircuit(2, name='Bell', metadata={'test': True})
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

with open('bell.qpy', 'wb') as fd:
    qpy.dump(qc, fd)

with open('bell.qpy', 'rb') as fd:
    new_qc = qpy.load(fd)[0]

La fonction qiskit.qpy.dump() vous permet également d'inclure plusieurs circuits dans un seul fichier QPY :

with open('twenty_bells.qpy', 'wb') as fd:
    qpy.dump([qc] * 20, fd)

et le chargement de ce fichier renverra une liste de tous les circuits

with open('twenty_bells.qpy', 'rb') as fd:
    twenty_new_bells = qpy.load(fd)

documentation d'API

load

qiskit.qpy.load(file_obj, metadata_deserializer=None, annotation_factories=None)

GitHub

Charger un fichier binaire QPY

Cette fonction est utilisée pour charger un fichier de programme QPY Qiskit sérialisé et créer des objets à partir de son contenu QuantumCircuit objets à partir de son contenu. Par exemple :

from qiskit import qpy

with open('bell.qpy', 'rb') as fd:
    circuits = qpy.load(fd)

ou avec un fichier compressé par gzip :

import gzip
from qiskit import qpy

with gzip.open('bell.qpy.gz', 'rb') as fd:
    circuits = qpy.load(fd)

qui lira le contenu du fichier qpy et renverra une liste d'objets du fichier QuantumCircuit objets du fichier.

Paramètres

  • file_obj (BinaryIO) – Un objet de type fichier qui contient les données binaires QPY d'un circuit.
  • metadata_deserializer (type[JSONDecoder] | None) – Une classe JSONDecoder facultative qui sera utilisée pour le paramètre cls « kwarg » de l'appel interne json.load servant à désérialiser la charge utile JSON utilisée pour l'attribut .metadata de tout programme figurant dans le fichier QPY. Si cela n'est pas spécifié, les métadonnées du circuit seront analysées au format JSON à l'aide de la fonction de la bibliothèque standard json.load() , en utilisant la classe par défaut JSONDecoder .
  • annotation_factories (Mapping[str, Callable[[], annotation.QPYSerializer]] | None) – Mise en correspondance des espaces de noms avec les fonctions qui créent de nouvelles instances de annotation.QPUSerializer, afin de gérer le chargement d'objets personnalisés Annotation .

Retours

La liste des programmes Qiskit contenus dans les données QPY. Une liste est toujours renvoyée, même s'il n'y a qu'un seul programme dans les données QPY.

Augmentations

  • QiskitError - si file_obj n'est pas un fichier QPY valide
  • TypeError - Lorsque le type de données chargé n'est pas valide.
  • MissingOptionalLibraryError – Si la symengine bibliothèque « engine » n'est pas installée lors du chargement d'une charge utile QPY de version 10, 11 ou 12 qui utilise le codage symbolique « symengine » et contient ParameterExpression des instances.
  • QpyError - si un type de données connu mais non pris en charge est chargé.

Type de retour

list[ QPY_TYPES_PRIS_EN_CHARGE]

dump

qiskit.qpy.dump(programs, file_obj, metadata_serializer=None, use_symengine=False, version=17, annotation_factories=None)

GitHub

Ecrire des données binaires QPY dans un fichier

Cette fonction permet d'enregistrer un circuit dans un fichier en vue d'une utilisation ultérieure ou d'un transfert entre machines. Le format QPY est rétrocompatible et peut être chargé avec les versions futures de Qiskit.

Par exemple :

from qiskit.circuit import QuantumCircuit
from qiskit import qpy

qc = QuantumCircuit(2, name='Bell', metadata={'test': True})
qc.h(0)
qc.cx(0, 1)
qc.measure_all()

à partir de là, vous pouvez écrire les données de qpy dans un fichier :

with open('bell.qpy', 'wb') as fd:
    qpy.dump(qc, fd)

ou un fichier compressé par gzip :

import gzip

with gzip.open('bell.qpy.gz', 'wb') as fd:
    qpy.dump(qc, fd)

Ce qui va enregistrer le circuit sérialisé qpy dans le fichier fourni.

Paramètres

  • programs (list[QPY_SUPPORTED_TYPES] | QPY_SUPPORTED_TYPES) – Objet(s) pris en charge par QPY à enregistrer dans le fichier spécifié sous la forme d'un objet. QPY prend en charge QuantumCircuit.

  • file_obj (BinaryIO) – L'objet de type fichier dans lequel écrire les données QPY

  • metadata_serializer (type[JSONEncoder] | None) – Une classe JSONEncoder facultative à laquelle sera transmis l'attribut .metadata de chaque élément du programs dictionnaire et qui sera utilisée comme argument cls clé (kwarg) lors de l'appel à la méthode json.dump ` afin de sérialiser ce dictionnaire au format JSON.

  • use_symengine (bool) – Cet indicateur n'est plus utilisé par les versions de QPY prises en charge par cette fonction et n'aura aucun impact sur la charge utile QPY générée, si ce n'est pour définir un champ inutilisé dans l'en-tête du fichier QPY v13.

  • version (int) –

    La version du format QPY à émettre. Par défaut, il s'agit du dernier format supporté de QPY_VERSIONcependant, pour des raisons de compatibilité, si vous devez charger la charge utile QPY générée avec une version plus ancienne de Qiskit, vous pouvez également sélectionner une version plus ancienne du format QPY jusqu'à la version d'exportation minimale prise en charge, qui ne peut changer que lors de la sortie d'une version majeure de Qiskit, afin de générer une version plus ancienne du format QPY. Vous pouvez accéder à la version actuelle de QPY et à la version minimale compatible avec qpy.QPY_VERSION et qpy.QPY_COMPATIBILITY_VERSION respectivement.

    Remarque

    Si elle est spécifiée avec une version plus ancienne de QPY, les limitations et les bogues potentiels liés au format QPY de cette version persisteront. Cette option ne doit être utilisée que si la compatibilité avec le chargement de la charge utile avec une ancienne version de Qiskit est nécessaire.

  • annotation_factories (Mapping[str, Callable[[], annotation.QPYSerializer]] | None) – Mise en correspondance des espaces de noms avec les fonctions qui créent de nouvelles instances de annotation.QPUSerializer, pour gérer la sauvegarde d'objets personnalisés Annotation . L'appel suivant à load() devra utiliser des objets sérialiseurs similaires, capables de prendre en charge le format de sortie personnalisé de ces sérialiseurs.

Augmentations

  • TypeError - Lorsque le type de données saisi n'est pas valide.
  • ValueError - Lorsqu'un numéro de version non pris en charge est transmis pour l'argument version .

get_qpy_version

qiskit.qpy.get_qpy_version(file_obj)

GitHub

Cette fonction identifie la version QPY du fichier.

Cette fonction lit l'en-tête de file_obj et renvoie la version du format QPY. Cela ne fera pas avancer le curseur de file_obj. Si vous l'utilisez pour une lecture ultérieure, par exemple pour appeler load()vous pouvez transmettre directement file_obj . Par exemple :

from qiskit import qpy

qpy_version = qpy.get_qpy_version(qpy_file)
if qpy_version > 12:
    qpy.load(qpy_file)

Paramètres

file_obj (BinaryIO) – Un objet de type fichier qui contient les données binaires QPY d'un circuit.

Retours

La version QPY du fichier spécifié.

Type de retour

int

Ces fonctions lèveront une sous-classe personnalisée de QiskitError si elles rencontrent des problèmes lors de la sérialisation ou de la désérialisation.

QpyError

exception qiskit.qpy.QpyError(*message)

GitHub

Bases : QiskitError

Erreurs soulevées par le module qpy.

Définir le message d'erreur.

Lorsqu'une version QPY cible inférieure au maximum est définie pour la sérialisation, mais que l'objet à sérialiser contient des caractéristiques qui ne peuvent pas être représentées dans ce format, une sous-classe de QpyError est déclenchée :

UnsupportedFeatureForVersion

exception qiskit.qpy.UnsupportedFeatureForVersion(feature, required, target)

GitHub

Bases : QpyError

Erreur QPY levée lorsque la version de dump cible est trop faible pour une fonctionnalité présente dans l'objet à sérialiser.

Paramètres

  • feature (str) – une description de l'élément problématique.
  • required (int) – la version minimale de QPY qui serait nécessaire pour représenter cette fonctionnalité.
  • target (int) – la version de QPY utilisée dans la sérialisation.

qiskit.qpy.QPY_VERSION

La version actuelle du format QPY à partir de cette version. C'est la valeur par défaut de l'argument du mot-clé version sur qpy.dump() et également la limite supérieure des valeurs acceptées pour le même argument. Il s'agit également de la limite supérieure des versions prises en charge par qpy.load().

Type

int

qiskit.qpy.QPY_COMPATIBILITY_VERSION

Version du format QPY actuellement compatible avec le minimum requis. Il s'agit de la version minimale que qpy.dump() acceptera pour le mot-clé version . qpy.load() pourra charger toutes les versions de format de QPY publiées (jusqu'à QPY_VERSION).

Type

int


Compatibilité QPY

Le format QPY est conçu pour être rétrocompatible. Cela signifie que vous devriez pouvoir charger un QPY avec n'importe quelle version de Qiskit plus récente que celle qui l'a généré. Cependant, le chargement d'un fichier QPY avec une ancienne version de Qiskit n'est pas pris en charge et peut ne pas fonctionner.

Par exemple, si vous avez généré un fichier QPY à l'aide de qiskit-terra 0.18.1, vous pouvez charger ce fichier QPY avec qiskit-terra 0.19.0 et un hypothétique qiskit-terra 0.29.0. Cependant, le chargement de ce fichier QPY à l'aide de 0.18.0 n'est pas pris en charge et peut ne pas fonctionner.

Il convient de noter que les métadonnées des circuits et les objets personnalisés Annotation sont sérialisés et désérialisés à l'aide de classes fournies par l'utilisateur, car ces objets sont entièrement personnalisables; leur compatibilité ascendante et descendante est donc limitée par ce que l'utilisateur fournit.

Si une fonctionnalité en cours de chargement est obsolète dans la version correspondante de qiskit, QPY lèvera un QPYLoadingDeprecatedFeatureWarning informant de la période de dépréciation et de la façon dont la fonctionnalité sera gérée en interne.

QPYLoadingDeprecatedFeatureWarning

exception qiskit.qpy.QPYLoadingDeprecatedFeatureWarning

GitHub

Bases : QiskitWarning

Avertissement de dépréciation visible pour QPY chargeant des fonctions sans point stable dans la pile d'appel.

Remarque

Dans les versions de Qiskit antérieures à 1.2.4, l'argument use_symengine=True de la fonction qpy.dump() pouvait poser des problèmes de rétrocompatibilité s'il y avait ParameterExpression des objets à sérialiser. Notamment :

  • Lorsque la version de chargement de Qiskit est 1.2.4 ou supérieure, les fichiers QPY générés avec n'importe quelle version de Qiskit >= 0.46.0 peuvent être chargés. Si une version de Qiskit comprise entre 0.45.0 et 0.45.3 a été utilisée pour générer les fichiers, et que l'argument non par défaut use_symengine=True a été donné à qpy.dump()le fichier ne peut être lu que si la version de symengine utilisée dans l'environnement de génération faisait partie de la série 0.11 ou 0.13, mais si l'environnement a été créé pendant la fenêtre de support de Qiskit 0.45, il est probable que symengine==0.9.2 ait été utilisé.
  • Lorsque la version de chargement de Qiskit est comprise entre 0.46.0 et 1.2.2 inclus, le fichier ne peut être lu que si la version installée de symengine dans l'environnement de chargement correspond à la version utilisée dans l'environnement de génération.

Pour récupérer un fichier QPY qui échoue en raison d'erreurs liées à la version de symengine lors d'un appel à qpy.load()essayez d'abord d'utiliser Qiskit >= 1.2.4 pour charger le fichier. Si l'échec persiste, c'est probablement parce que Qiskit 0.45.x a été utilisé pour générer le fichier avec use_symengine=True. Dans ce cas, utilisez Qiskit 0.45.3 avec symengine==0.9.2 pour charger le fichier, puis réexportez-le vers QPY en utilisant use_symengine=False. Le fichier résultant peut alors être chargé par n'importe quelle version ultérieure de Qiskit.

Remarque

À partir de la version 2.0.0 de Qiskit, qui a supprimé le module Pulse de la bibliothèque, QPY fournit un support limité pour le chargement de charges utiles comprenant des données d'impulsion. Chargement d'une charge utile ScheduleBlock , une QpyError sera levée. Lors du chargement d'une charge utile pour un circuit contenant des portes d'impulsion, le circuit de sortie contiendra des instructions personnalisées sans données d'étalonnage pour chaque porte d'impulsion, ce qui les rendra indéfinies.

Historique des versions du format QPY

Si vous envisagez de charger un fichier QPY entre différentes versions de Qiskit, il est utile de savoir quelles versions étaient disponibles dans une version donnée. Comme le QPY est compatible en amont mais pas en aval, vous devez vous assurer qu'une version donnée du format QPY a été publiée dans la version que vous appelez load() avec laquelle vous appelez. Le tableau suivant énumère les versions de QPY qui ont été prises en charge dans chaque version de Qiskit (et qiskit-terra avant Qiskit 1.0.0 ) depuis l'introduction de QPY dans qiskit-terra 0.18.0.

Version Qiskit (qiskit-terra pour < 1.0.0 )
dump() format(s) versions de sortie
load() version maximale supportée (les versions plus anciennes peuvent toujours être lues)
2.5.213, 14, 15, 16, 1717
2.5.113, 14, 15, 16, 1717
2.5.013, 14, 15, 16, 1717
2.4.113, 14, 15, 16, 1717
2.4.013, 14, 15, 16, 1717
2.3.113, 14, 15, 16, 1717
2.3.013, 14, 15, 16, 1717
2.2.213, 14, 15, 1616
2.2.113, 14, 15, 1616
2.2.013, 14, 15, 1616
2.1.213, 14, 15, 1616
2.1.113, 14, 15, 1616
2.1.013, 14, 1530
2.0.213, 1414
2.0.113, 1414
2.0.013, 1414
1.4.310, 11, 12, 1313
1.4.210, 11, 12, 1313
1.4.110, 11, 12, 1313
1.4.010, 11, 12, 1313
1.3.310, 11, 12, 1313
1.3.210, 11, 12, 1313
1.3.110, 11, 12, 1313
1.3.010, 11, 12, 1313
1.2.410, 11, 1212
1.2.3 (arraché)10, 11, 1212
1.2.210, 11, 1212
1.2.110, 11, 1212
1.2.010, 11, 1212
1.1.010, 11, 1212
1.0.210, 1111
1.0.110, 1111
1.0.010, 1111
0.46.11010
0.45.31010
0.45.21010
0.45.11010
0.45.01010
0.25.399
0.25.299
0.25.199
0.24.288
0.24.177
0.24.077
0.23.366
0.23.266
0.23.166
0.23.066
0.22.455
0.22.355
0.22.255
0.22.155
0.22.055
0.21.255
0.21.155
0.21.055
0.20.244
0.20.144
0.20.044
0.19.244
0.19.133
0.19.022
0.18.311
0.18.211
0.18.111
0.18.011

Format QPY

Le format de sérialisation QPY est un format de sérialisation binaire portable et multiplateforme pour les objets de Qiskit QuantumCircuit objets dans Qiskit. Le format de base du fichier est le suivant :

Un fichier QPY (ou un objet mémoire) commence toujours par la chaîne de 6 octets UTF8 suivante : QISKIT qui est immédiatement suivie par l'en-tête général du fichier. Le contenu de l'en-tête du fichier, tel qu'il est défini dans une structure C, est le suivant :

struct {
    uint8_t qpy_version;
    uint8_t qiskit_major_version;
    uint8_t qiskit_minor_version;
    uint8_t qiskit_patch_version;
    uint64_t num_circuits;
}

À partir de V10, un nouveau champ est ajouté à la structure d'en-tête du fichier pour représenter le schéma d'encodage utilisé pour les expressions symboliques :

struct {
    uint8_t qpy_version;
    uint8_t qiskit_major_version;
    uint8_t qiskit_minor_version;
    uint8_t qiskit_patch_version;
    uint64_t num_circuits;
    char symbolic_encoding;
}

À partir de V16, la structure d'en-tête du fichier est immédiatement suivie d'une table de début de circuit contenant les décalages d'octets de chaque charge utile de circuit dans le fichier. La table de démarrage du circuit comporte num_circuits entrées, chacune d'entre elles étant du type uint64_t. Dans toutes les versions précédentes, l'en-tête du fichier est immédiatement suivi par les charges utiles du circuit dans l'ordre, sans aucun remplissage entre les deux.

Toutes les valeurs utilisent l'ordre des octets réseau [1] (big-endian) pour assurer la compatibilité entre les plateformes. La seule exception concerne les versions du format QPY <= 17 : le codage des entiers et des nombres à virgule flottante dans ce format INSTRUCTION_PARAM est de type « little endian ».

Chaque circuit individuel est composé des éléments suivants, dans l'ordre, de haut en bas :

HEADER
METADATA
REGISTERS
ANNOTATION_HEADER
STANDALONE_VARS
CUSTOM_DEFINITIONS
INSTRUCTIONS

Changement dans la version QPY : 15 ANNOTATION_HEADER a été ajouté entre REGISTERS et STANDALONE_VARS.

Changement dans la version QPY : 12 STANDALONE_VARS a été ajouté entre REGISTERS et CUSTOM_DEFINITIONS.

Il y a une charge utile de circuit pour chaque circuit (dont le nombre total est dicté par num_circuits dans l'en-tête du fichier). Il n'y a pas de remplissage entre les circuits dans les données.

Version 17

La version 17 prend désormais en charge la sérialisation et la désérialisation PauliEvolutionGate des SparseObservable objets contenant des opérateurs.

Modifications apportées à PAULI_EVOLUTION

Le format de PAULI_EVOLUTION reste inchangé, mais celui des opérateurs qui suivent immédiatement la porte d'évolution condensée a été mis à jour. Au lieu des éléments operator_count définis selon le format SPARSE_PAULI_OP_LIST_ELEM, la charge utile précise désormais le type de chaque opérateur afin de prendre en compte soit les opérateurs de type SparsePauliOp soit ceux de type SparseObservable.

La nouvelle charge utile qui suit PAULI_EVOLUTION contient désormais exactement operator_count séquences composées d'un bool ("!?") suivi de l'opérateur. Si la valeur booléenne est True, l'opérateur est un SparseObservable et est interprété selon le nouveau format SPARSE_OBSERVABLE (voir ci-dessous). FalseSi c'est le cas, l'opérateur est un SparsePauliOp et est interprété conformément au format SPARSE_PAULI_OP_LIST_ELEM existant.

Nouveau SPARSE_OBSERVABLE

Le format SPARSE_OBSERVABLE représente une instance d'un SparseObservable, définie par

struct {
  uint32_t num_qubits;
  uint64_t coeff_data_len;
  uint64_t bitterm_data_len;
  uint64_t inds_data_len;
  uint64_t bounds_data_len;
}

qui est immédiatement suivi du nombre de qubits, puis des tableaux de données des coefficients, des termes binaires, des indices et des limites de l'observable. Le format spécifie le nombre d'octets occupés par chaque tableau. Le nombre d'éléments peut être calculé en divisant le nombre d'octets par la taille de chaque élément.

  • Chaque coefficient est stocké sous la forme de deux éléments «!d » consécutifs, d'abord la partie réelle, puis la partie imaginaire.
  • Les éléments du terme binaire sont de type «!« H » et représente la valeur d' u8 de la SparseObservable.BitTerm
  • Les éléments des indices sont de type «!I ».
  • Les éléments de délimitation sont de type «!Q ».

Version 16

La version 16 ajoute une table de démarrage de circuit au format de fichier QPY. Il sert d'index des décalages d'octets de chaque charge utile de circuit dans le fichier. La motivation de ce changement est de permettre un chargement plus efficace des circuits utilisant le multithreading dans une future implémentation Rust du désérialiseur QPY.

Modifications apportées à la DURÉE

Une nouvelle variante a été ajoutée à l'encodage existant du type DURATION pour les picosecondes. Il est codé comme suit et s'ajoute aux variantes précédemment prises en charge.

Classe Qiskit
Code du type
Contenu utile
pspUn double value.

Version 15

La version 15 ajoute le concept d'annotations personnalisées au format de la charge utile. QPY lui-même ne précise pas comment les annotations sont sérialisées ou désérialisées, puisqu'il s'agit d'objets utilisateur personnalisés. Le format coopère cependant avec les sous-sérialisateurs.

La version 15 ajoute le champ ANNOTATION_HEADER entre les champs STANDALONE_VARS et CUSTOM_DEFINITIONS au niveau supérieur de la charge utile d'un circuit unique. Il modifie l'interprétation d'un champ de la structure INSTRUCTION d'une manière compatible avec l'ABI et ajoute à INSTRUCTION un trailer INSTRUCTION_ANNOTATIONS dont la présence est conditionnée par la présence d'un bit défini dans la charge utile INSTRUCTION .

Nouvelle ANNOTATION_HEADER

Le champ ANNOTATION_HEADER est une charge utile de taille variable dans l'en-tête. Elle commence par une instance de ANNOTATION_HEADER_STATIC, qui est la structure C :

struct ANNOTATION_HEADER_STATIC {
    uint32_t num_namespaces;
}

Il est immédiatement suivi par num_namespaces instances de la charge utile ANNOTATION_STATE . L'ordre de ces éléments est important et doit être conservé pendant le processus de désérialisation, car les charges utiles INSTRUCTION_ANNOTATION suivantes seront indexées dans cet ordre.

La charge utile de ANNOTATION_STATE commence par la structure C fixe :

struct ANNOTATION_STATE_HEADER {
    uint32_t namespace_size;
    uint64_t state_size;
}

Cet en-tête est immédiatement suivi de namespace_size octets de texte codé UTF-8, qui constituent l'espace de noms. Ces octets sont immédiatement suivis de state_size octets de données arbitraires. Le format de cette charge utile "état" n'est pas défini par le QPY. Cette responsabilité incombe plutôt à un objet externe associé à l'espace de noms stocké. Le format ne dicte pas comment produire ces objets; les annotations étant entièrement personnalisées, l'utilisateur doit fournir les méthodes de sérialisation et de désérialisation.

Modifications apportées à l'INSTRUCTION

La structure INSTRUCTION est modifiée de manière compatible avec l'ABI par rapport à sa définition précédente dans la version 9. La nouvelle structure est la structure C (rappelons qu'il n'y a pas de remplissage entre les champs, ni à la fin de la structure) :

struct INSTRUCTION {
    uint16_t name_size;
    uint16_t label_size;
    uint16_t num_parameters;
    uint32_t num_qargs;
    uint32_t num_cargs;
    uint8_t extras_key;
    uint16_t conditional_reg_name_size;
    int64_t conditional_value;
    uint32_t num_ctrl_qubits;
    uint32_t ctrl_state;
}

où le champ uint8_t extras_key remplace le précédent uint8_t conditional_key. La différence est purement interprétative. Les deux bits inférieurs de l'octet sont toujours interprétés comme définissant la condition et son type. Le bit de poids fort de l'octet est désormais un drapeau, qui indique si un champ INSTRUCTION_ANNOTATIONS_HEADER est présent (si le bit est activé) dans les données de fin de la structure INSTRUCTION .

Une instruction complète apparaît dans le flux de données, y compris les objets de fin et sans aucun octet de remplissage entre les éléments, comme :

struct INSTRUCTION;
uint8_t name[name_size];
uint8_t label[label_size];
uint8_t register[conditional_reg_name_size]; (1)
struct INSTRUCTION_PARAM;                    (2)
struct INSTRUCTION_ARG[num_qargs];
struct INSTRUCTION_ARG[num_cargs];
struct INSTRUCTION_PARAM[num_parameters];
INSTRUCTION_ANNOTATIONS;                     (3)

Les remarques suivantes s'appliquent :

  1. si les deux bits de poids faible de extras_key ont la valeur 2, indiquant que la condition est une EXPRESSION, conditional_reg_name_size est toujours zéro.
  2. ce champ est présent si et seulement si les deux bits de poids faible de extras_key ont la valeur 2, ce qui indique que la condition est une EXPRESSION.
  3. ce champ est présent si et seulement si le bit de poids fort de extras_key est activé. Ce champ a une taille variable; voir Nouvelle INSTRUCTION_ANNOTATIONS.

Nouvelles INSTRUCTIONS_ANNOTATIONS

La charge utile de INSTRUCTION_ANNOTATIONS commence par la structure C :

struct INSTRUCTION_ANNOTATIONS_HEADER {
    uint32_t num_annotations;
}

Cette charge utile est immédiatement suivie de num_annotations instances de la charge utile INSTRUCTION_ANNOTATION , qui est de taille variable.

La charge utile INSRTUCTION_ANNOTATION est définie par la structure C suivante, à laquelle s'ajoute un nombre d'octets égal à payload_size, appelé ANNOTATION_PAYLOAD.

struct INSTRUCTION_ANNOTATION {
    uint32_t namespace_index;
    uint32_t payload_size;
}

L'adresse namespace_index est un index entier dans la liste des objets ANNOTATION_NAMESPACE définis dans l'adresse ANNOTATION_HEADER. L'espace de noms de la sérialisation pour une annotation est la chaîne encodée UTF-8 dans la charge utile correspondante.

Le format de l'objet ANNOTATION_PAYLOAD n'est pas spécifié par QPY. Il est défini par un objet de sérialisation externe associé à l'espace de noms référencé par le namespace_index et son état de sérialisation associé dans le ANNOTATION_HEADER.

Changements au sein d' PARAM_EXPR_ELEM_V13

La structure elle-même reste inchangée. Cependant, pour un PARAM_EXPR_ELEM_V13 représentant un ParameterExpression.subs() appel (avec op_code = 15, et donc lhs_type = 'p' et rhs_type = 'n'), le MAPPING final mappe désormais les clés des octets bruts des Parameter UUID vers les valeurs substituées. Auparavant (dans les versions 13 et 14 de QPY), ce mappage stockait les noms des paramètres en tant que clés.

Version 14

La version 14 ajoute un nouveau type de base DURATION, la prise en charge de classes supplémentaires Type classes Float et Durationet un nouveau type de nœud d'expression Stretch.

DUREE

Un Duration est codé par un ASCII char d'un seul octet qui code le type de type, suivi d'une charge utile qui varie en fonction du type. Les codes définis sont les suivants

Classe Qiskit
Code du type
Contenu utile
dttUn unsigned long long value.
nsnUn double value.
usuUn double value.
msmUn double value.
ssUn double value.

Modifications apportées à EXPR_VAR_DECLARATION

Le type EXPR_VAR_DECLARATION est désormais utilisé pour représenter à la fois les Var les variables autonomes et les Stretch et les identificateurs. Pour prendre en charge cette modification, le code de type d'utilisation comporte deux nouvelles entrées possibles, en plus des entrées existantes :

Code du type
Signification
ALe circuit s'étend jusqu'à capture .
OUn tronçon du circuit déclaré localement.

Modifications apportées à EXPRESSION

Le code de type EXPRESSION comporte une nouvelle entrée possible, s, correspondant aux expr.Stretch nœuds.

Classe Qiskit
Code du type
Contenu utile
Enfants
StretchsUn unsigned short var_index0

Modifications apportées à EXPR_TYPE

Le tableau suivant montre les nouvelles classes de types ajoutées dans la version :

Classe Qiskit
Code du type
Contenu utile
FloatfNéant.
DurationdNéant.

Modifications apportées à EXPR_VALUE

Le système de types de l'expression classique prend désormais en charge de nouveaux types d'encodage pour les valeurs littérales, en plus des encodages existants pour int et bool. Les nouveaux encodages des types de valeurs sont indiqués ci-dessous :

Python type
Code du type
Contenu utile
floatfUn double value.
DurationtUn DURATION.

Version 13

La version 13 a ajouté une représentation de sérialisation native Qiskit pour ParameterExpression. Les versions précédentes de QPY utilisaient soit sympy soit symengine pour sérialiser l'expression symbolique sous-jacente. À partir de la version 13, QPY représente désormais la séquence d'appels API utilisée pour créer le fichier ParameterExpression.

Le principal changement apporté au format de sérialisation concerne la charge utile PARAMETER_EXPR. Les expr_size octets qui suivent l'en-tête contiennent désormais un tableau de PARAM_EXPR_ELEM_V13 structures. L'objectif est que ce tableau soit lu une structure à la fois, chaque structure décrivant l'un des appels à effectuer pour reconstruire le ParameterExpression.

PARAM_EXPR_ELEM_V13

Le format struct est défini comme suit :

struct {
    unsigned char op_code;
    char lhs_type;
    char lhs[16];
    char rhs_type;
    char rhs[16];
} PARAM_EXPR_ELEM_V13;

Le op_code champ sert à définir l'opération ajoutée au ParameterExpression. La valeur peut être :

op_code
0__add__()
1__sub__()
2__mul__()
3__truediv__()
4__pow__()
5sin()
6cos()
7tan()
8arcsin()
9arccos()
10exp()
11log()
12sign()
13gradient()
14conjugate()
30subs()
16abs()
17arctan()
255NULL

La valeur de 255 ( NULL ) n'est utilisée que pour remplir le champ du code d'opération pour les entrées qui ne sont pas des opérations réelles mais qui indiquent des définitions récursives. Les champs lhs_type et rhs_type sont utilisés pour décrire les types d'opérandes et peuvent contenir l'un des caractères codés UTF-8 suivants :

Valeur
Type
nNone
pParameter
ffloat
ccomplex
iint
sDébut de la définition récursive ParameterExpression
eFin de la définition récursive ParameterExpression
usubstitution

Si la valeur du type est f, c, ou i, les largeurs de champ correspondantes lhs ou rhs sont de 128 bits chacune. Dans le cas des nombres à virgule flottante, la valeur littérale est encodée sous la forme d'un nombre de type double avec un remplissage par des zéros, tandis que les nombres complexes sont encodés en plaçant la partie réelle suivie de la partie imaginaire, chacune occupant 64 bits. iEn effet, la valeur est codée sous la forme d'un entier signé de 64 bits, complété par des zéros pour atteindre la largeur totale de 128 bits. n est utilisé pour représenter un None et n'est généralement pas utilisé directement, car il indique un argument qui n'est pas utilisé. En effet, p cette donnée correspond à l'UUID de l'élément Parameter , qui peut être recherché dans la table de symboles décrite dans la map_elements charge utile externe PARAMETER_EXPR. ParameterExpressionSi la valeur du type est s , cela marque le début d'une nouvelle section récursive pour un élément imbriqué. Par exemple, dans l'extrait de code suivant, il y a un élément interne expr contenu dans final_expr, ce qui constitue une expression imbriquée :

from qiskit.circuit import Parameter

x = Parameter("x")
y = Parameter("y")
z = Parameter("z")

expr = (x + y) / 2
final_expr = z**2 + expr

Lorsque s apparaît, cela signifie que, jusqu'à ce qu'un e` struct is reached, the next structs are used for a recursive definition. For both ``s et e un soient définis, les valeurs des données ne sont pas utilisées et sont toujours fixées à 0. La valeur de u type sert à représenter un appel de substitution. Ceci est utilisé uniquement pour lhs_type et est toujours associé à un rhs_type de n. La valeur de la donnée correspond à la taille, en octets, d'un mappage codé selon le format MAPPING, qui associe Parameter les noms à leurs valeurs pour l'appel subs() . Les données de mappage se trouvent juste après la structure, et la structure suivante commence immédiatement après ces données.

Version 12

La version 12 ajoute la prise en charge de :

  • circuits contenant des variables à mémoire expr.Var mémoire.

Modifications apportées à l'EN-TÊTE

La structure HEADER d'un circuit individuel a ajouté trois comptes uint32_t des variables d'entrée, capturées et déclarées localement dans le circuit. Le nouveau formulaire se présente comme suit :

struct {
    uint16_t name_size;
    char global_phase_type;
    uint16_t global_phase_size;
    uint32_t num_qubits;
    uint32_t num_clbits;
    uint64_t metadata_size;
    uint32_t num_registers;
    uint64_t num_instructions;
    uint32_t num_vars;
} HEADER_V12;

La structure HEADER_V12 est immédiatement suivie du même nom, de la même phase globale, des mêmes métadonnées et des mêmes informations de registre que la version V2 de l'en-tête. Immédiatement après les registres se trouve num_vars instances de EXPR_VAR_STANDALONE qui définissent les variables de ce circuit. Ensuite, les données se poursuivent avec des définitions et des instructions personnalisées, comme dans les versions antérieures de QPY.

EXPR_VAR_DECLARATION

Une adresse EXPR_VAR_DECLARATION définit une instance expr.Var qui est autonome, c'est-à-dire qu'elle représente un emplacement de mémoire qui lui appartient en propre, plutôt que d'envelopper une instance de Clbit ou ClassicalRegister. La charge utile est une structure C :

struct {
    char uuid_bytes[16];
    char usage;
    uint16_t name_size;
}

qui est immédiatement suivie d'une charge utile EXPR_TYPE , puis de name_size octets de données de chaîne de codage UTF-8 contenant le nom de la variable.

Le code du type d'utilisation char prend les valeurs suivantes :

Code du type
Signification
IUne variable input pour le circuit.
CUne variable capture pour le circuit.
LVariable déclarée localement pour le circuit.

Modifications apportées à EXPR_VAR

La variable EXPR_VAR a été dotée d'un nouveau code de type et d'une nouvelle charge utile, qui s'ajoutent aux codes préexistants :

Python classe
Code du type
Contenu utile
UUIDUUn index uint32_t de la variable dans la série de variables EXPR_VAR_STANDALONE qui ont été écrites immédiatement après l'en-tête du circuit.

Notamment, ce nouveau code type indexe des variables prédéfinies de l'en-tête du circuit, plutôt que de redéfinir la variable à chaque endroit où elle est utilisée.

Modifications apportées à EXPRESSION

Le code de type EXPRESSION comporte une nouvelle entrée possible, i, correspondant aux expr.Index nœuds.

Classe Qiskit
Code du type
Contenu utile
Enfants
IndexiPas de charge utile supplémentaire. Les enfants sont la cible et l'index, dans cet ordre.2

Version 11

La version 11 est identique à la version 10, à l'exception des points suivants. Tout d'abord, les noms figurant dans les blocs CUSTOM_INSTRUCTION sont suivis d'un suffixe de la forme "_{uuid_hex}" où uuid_hex est une chaîne hexadécimale de type UUID, telle que celle renvoyée par UUID.hex. Par exemple : "b3ecab5b4d6a4eb6bc2b2dbf18d83e1e". Deuxièmement, il ajoute la prise en charge des AnnotatedOperation objets. L'opération de base d'une opération annotée est stockée à l'aide du bloc INSTRUCTION, et une valeur 'a'``is added to indicate that the custom instruction is an annotated operation. The list of modifiers are stored as instruction parameters using INSTRUCTION_PARAM, with an additional value ``'m' supplémentaire type est ajoutée pour indiquer que le paramètre est de type Modifier. Chaque modificateur est stocké à l'aide de la structure MODIFIER.

Modificateur

Cela représente Modifier

struct {
    char type;
    uint32_t num_ctrl_qubits;
    uint32_t ctrl_state;
    double power;
}

Cela suffit pour stocker les différents types de modificateurs nécessaires à la sérialisation des objets de type AnnotatedOperation. Le champ type peut prendre la valeur 'i', 'c' ou 'p', indiquant respectivement si le modificateur est un modificateur inverse, un modificateur de contrôle ou un modificateur de puissance. Dans le deuxième cas, les champs num_ctrl_qubits et ctrl_state définissent la logique de contrôle de l'opération de base, et dans le troisième cas, le champ power représente la puissance de l'opération de base.

Version 10

La version 10 ajoute la prise en charge de :

  • Sérialisation native symengine pour les objets de type ParameterExpression ainsi que pour les expressions symboliques dans les blocs de planification Pulse.
  • nouveaux champs ajoutés à la TranspileLayout classe dans la version « 0.45.0 » de Qiskit.

Le champ symbolic_encoding est ajouté à l'en-tête du fichier, et un nouveau type d'encodage char est introduit, associé à chaque bibliothèque symbolique comme suit : p fait référence à l'encodage sympy et e fait référence à l'encodage symengine.

Modifications apportées à FILE_HEADER

Le contenu de FILE_HEADER après V10 est défini comme une structure C sous la forme suivante :

struct {
    uint8_t qpy_version;
    uint8_t qiskit_major_version;
    uint8_t qiskit_minor_version;
    uint8_t qiskit_patch_version;
    uint64_t num_circuits;
    char symbolic_encoding;
} FILE_HEADER_V10;

Modifications apportées à la MISE EN PAGE

La structure LAYOUT est mise à jour et comporte désormais un champ input_qubit_count supplémentaire. Avec la version 10, la structure LAYOUT est maintenant :

struct {
    char exists;
    int32_t initial_layout_size;
    int32_t input_mapping_size;
    int32_t final_layout_size;
    uint32_t extra_registers;
    int32_t input_qubit_count;
}

Le reste des données de mise en page après la LAYOUT structure est représenté comme dans les versions précédentes. NoneSi input qubit_count est < 0, cela signifie que et _output_qubit_list dans l'objet TranspileLayout sont tous _input_qubit_count deux.

Version 9

La version 9 ajoute la prise en charge des nœuds Expr classiques et leurs Types.

Expression

Un nœud Expr est représenté par un flux de données à largeur variable. Un nœud lui-même est représenté par (dans l'ordre du flux d'octets) :

  1. un discriminateur de code de type d'un octet;
  2. un objet EXPR_TYPE;
  3. une charge utile supplémentaire spécifique à un code de type;
  4. un nombre de charges utiles d'EXPRESSION enfant spécifiques au code de type (le nombre de ces charges est implicite dans le code de type et n'est pas explicitement stocké).

Chacun d'entre eux est décrit dans le tableau suivant :

Classe Qiskit
Code du type
Contenu utile
Enfants
VarxUn EXPR_VAR.0
ValuevUn EXPR_VALUE.0
CastcUn _Bool qui correspond à la valeur de implicit.1
UnaryuUn site uint8_t ayant la même valeur numérique que le fichier Unary.Op.1
BinarybUn site uint8_t ayant la même valeur numérique que le fichier Binary.Op.2

EXPR_TYPE

A Type est codé par un seul octet ASCII char qui code le type de type, suivi d'une charge utile qui varie en fonction du type. Les codes définis sont les suivants

Classe Qiskit
Code du type
Contenu utile
BoolbNéant.
UintuUn uint32_t width.

EXPR_VAR

Il s'agit d'une variable d'exécution d'un Var nœud. Il s'agit d'un code de type, suivi d'une charge utile spécifique au code de type :

Python classe
Code du type
Contenu utile
ClbitCUn uint32_t index qui est l'indice du Clbit dans le circuit contenant.
ClassicalRegisterRUn uint16_t reg_name_size, suivi d'autant d'octets de données de chaîne UTF-8 du nom du registre.

EXPR_VALUE

Cela représente un objet littéral dans le système de type classique, tel qu'un entier. À l'heure actuelle, il n'existe que très peu d'éléments littéraux de ce type. Ils sont codés sous la forme d'un code de type, suivi d'une charge utile spécifique au code de type.

Python type
Code du type
Contenu utile
boolbUn _Bool value.
intiUn uint8_t num_bytes, suivi du nombre entier codé dans autant d'octets (ordre du réseau) dans une représentation en complément à deux.

Modifications apportées à l'INSTRUCTION

Afin de prendre en charge l'utilisation de Expr nœuds dans les champs IfElseOp.condition, WhileLoopOp.condition et SwitchCaseOp.target, la structure INSTRUCTION est modifiée de manière compatible avec l'ABI pour revenir à sa définition précédente. La nouvelle structure est la structure C suivante :

struct {
    uint16_t name_size;
    uint16_t label_size;
    uint16_t num_parameters;
    uint32_t num_qargs;
    uint32_t num_cargs;
    uint8_t conditional_key;
    uint16_t conditional_reg_name_size;
    int64_t conditional_value;
    uint32_t num_ctrl_qubits;
    uint32_t ctrl_state;
}

où le seul changement est qu'une entrée uint8_t conditional_key a remplacé _Bool has_conditional. Ce nouveau site conditional_key prend les valeurs numériques suivantes, avec les effets suivants :

Valeur
Effets
0Le champ .condition de l'instruction est à None. Les champs conditional_reg_name_size et conditional_value doivent être ignorés.
1Le champ .condition de l'instruction est défini sur un 2-tuplet de a Clbit ou a ClassicalRegisteret un entier de valeur conditional_value. La charge utile INSTRUCTION, y compris ses données de fin, est analysée exactement comme elle le serait dans les versions de QPY inférieures à 8.
2L'instruction a son champ .condition défini sur un nœud Expr nœud. Les champs conditional_reg_name_size et conditional_value doivent être ignorés. Les données qui suivent la structure sont suivies (comme dans les versions de QPY inférieures à 9) par name_size octets de données de chaîne UTF-8 pour le nom de la classe et label_size octets de données de chaîne UTF-8 pour l'étiquette (s'il y en a une). Ensuite, il y a une INSTRUCTION_PARAM, qui contiendra une EXPRESSION. Ensuite, l'analyse se poursuit avec les structs INSTRUCTION_ARG, comme dans les versions précédentes de QPY.

Modifications apportées à INSTRUCTION_PARAM

Un nouveau code de type x est ajouté pour définir un paramètre EXPRESSION.

version 8

La version 8 prend désormais en charge la gestion d'un élément TranspileLayout stocké dans l'attribut QuantumCircuit.layout . Dans la version 8, juste après le bloc de calibrage situé à la fin de la charge utile du circuit, on trouve désormais la LAYOUT structure. Cette structure décrit la taille des trois attributs d'une TranspileLayout classe.

DISPOSITION

struct {
    char exists;
    int32_t initial_layout_size;
    int32_t input_mapping_size;
    int32_t final_layout_size;
    uint32_t extra_registers;
}

Si l'une des valeurs signées est -1 , cela signifie que l'attribut correspondant est None.

Immédiatement après la LAYOUT structure struct, on trouve une structure REGISTERS destinée extra_registers aux définitions de registres autonomes (plus précisément au format introduit dans la version 4 ) qui ne figurent pas dans le circuit. Il existe initial_layout_size INITIAL_LAYOUT_BIT également des structures permettant de définir cet TranspileLayout.initial_layout attribut.

BIT_DE_DISPOSITION_INITIALE

struct {
    int32_t index;
    int32_t register_size;
}

La valeur -1 indique None (c'est-à-dire qu'aucun registre n'est associé au bit). Chaque structure INITIAL_LAYOUT_BIT est suivie de register_size octets pour une chaîne codée utf8 pour le nom du registre.

La disposition initiale est suivie d'un tableau input_mapping_size d'entiers uint32_t représentant les positions des bits physiques de la disposition initiale. Cela permet de construire une liste de bits virtuels où l'index du tableau est sa position de mappage d'entrée.

Enfin, il y a un tableau d'entiers final_layout_size uint32_t . Chaque élément est un index dans l'attribut qubits du circuit qui permet de construire une correspondance entre la position de départ du qubit et la position de sortie à la fin du circuit.

Version 7

La version 7 ajoute la prise en charge de l'instruction Reference et la sérialisation d'un programme ScheduleBlock tout en conservant sa référence aux sous-programmes :

from qiskit import pulse
from qiskit import qpy

with pulse.build() as schedule:
    pulse.reference("cr45p", "q0", "q1")
    pulse.reference("x", "q0")
    pulse.reference("cr45p", "q0", "q1")

with open('template_ecr.qpy', 'wb') as fd:
    qpy.dump(schedule, fd)

Le modèle de données SCHEDULE_BLOCK conventionnel est préservé, mais dans la version 7, il est immédiatement suivi d'un bloc MAPPING supplémentaire utf8 octets représentant les données des sous-programmes référencés.

Un nouveau caractère clé de type est ajouté au groupe SCHEDULE_BLOCK_INSTRUCTIONS pour l'instruction Reference .

  • y: Reference instruction

Un nouveau type de caractère clé est ajouté au groupe SCHEDULE_BLOCK_OPERANDS pour les opérandes de l'instruction Reference , qui est un tuple de chaînes, par exemple ( “cr45p”, “q0”, “q1” ).

  • o: string (chaîne de l'opérande)

Notez qu'il s'agit du même codage que la chaîne intégrée Python. Cependant, le codage de valeur standard dans QPY utilise le caractère de type s pour les données de chaîne, ce qui entre en conflit avec SymbolicPulse dans la portée des opérandes de l'instruction d'impulsion. Un caractère de type spécial o est réservé aux données de la chaîne qui apparaissent dans les opérandes de l'instruction d'impulsion.

En outre, la version 7 ajoute deux nouvelles clés de type à la structure INSTRUCTION_PARM. "d" n'est suivi d'aucune donnée et représente la valeur littérale CASE_DEFAULT pour la prise en charge des déclarations de commutation. "R" représente un ClassicalRegister ou Clbitet est suivi du même format que la description du registre ou du bit classique utilisé dans le premier élément de la condition d'un champ INSTRUCTION.

Version 6

La version 6 ajoute la prise en charge de ScalableSymbolicPulse. Ces objets sont enregistrés et lus comme des objets SymbolicPulse, et le nom de la classe est ajouté aux données pour gérer correctement la sélection de la classe.

SymbolicPulse commence maintenant par l'en-tête SYMBOLIC_PULSE_V2 :

struct {
    uint16_t class_name_size;
    uint16_t type_size;
    uint16_t envelope_size;
    uint16_t constraints_size;
    uint16_t valid_amp_conditions_size;
    _bool amp_limited;
}

Le seul changement par rapport à la version 5 est l'ajout de la classe "nom". L'en-tête est immédiatement suivi de class_name_size utf8 octets avec le nom de la classe. Actuellement, SymbolicPulse ou ScalableSymbolicPulse sont pris en charge. Le reste des données est alors identique à la version 5.

Version 5

La version 5 diffère de la version 4 en ajoutant la prise en charge de ScheduleBlock et en modifiant deux charges utiles : la charge utile de métadonnées INSTRUCTION et le bloc CUSTOM_INSTRUCTION. Ces derniers comportent désormais de nouveaux champs permettant de mieux prendre en compte ControlledGate les objets présents dans un circuit. De plus, une nouvelle charge utile MAP_ITEM est définie pour implémenter le bloc MAPPING.

Remarque

La prise en charge de la représentation des calendriers d'impulsions et des calibrations personnalisées a été supprimée dans Qiskit v2.0. Lors du chargement des charges utiles QPY, ces champs de données sont désormais ignorés ou génèrent une erreur lors de l'utilisation de Qiskit pour la désérialisation.

Dans la version 5 et supérieure de QPY,

struct {
    char type;
}

suit immédiatement le bloc d'en-tête du fichier pour représenter le type de programme stocké dans le fichier.

  • Lorsque type==c, QuantumCircuit la charge utile suit
  • Lorsque type==s, ScheduleBlock charge utile suit
Remarque

Des programmes différents ne peuvent pas être regroupés dans le même fichier. Vous devez créer des fichiers différents pour chaque type de programme. Plusieurs objets du même type peuvent être enregistrés dans un seul fichier.

CALENDRIER_BLOC

ScheduleBlock est pris en charge pour la première fois dans la version 5 de QPY. Cela permet aux utilisateurs d'enregistrer des programmes d'impulsions au format binaire QPY comme suit :

from qiskit import pulse, qpy

with pulse.build() as schedule:
    pulse.play(pulse.Gaussian(160, 0.1, 40), pulse.DriveChannel(0))

with open('schedule.qpy', 'wb') as fd:
    qpy.dump(schedule, fd)

with open('schedule.qpy', 'rb') as fd:
    new_schedule = qpy.load(fd)[0]

Notez que le circuit et le bloc de programmation sont sérialisés et désérialisés via la même interface QPY. Le type de données d'entrée est analysé implicitement et aucune option supplémentaire n'est nécessaire pour enregistrer le bloc de programmation.

SCHEDULE_BLOCK_HEADER

ScheduleBlock commence par l'en-tête suivant :

struct {
    uint16_t name_size;
    uint64_t metadata_size;
    uint16_t num_element;
}

qui est immédiatement suivi par name_size utf8 octets du nom de l'horaire et metadata_size utf8 octets du dictionnaire de métadonnées sérialisées JSON attaché à l'horaire.

CALENDRIER_BLOC_ALIGNEMENTS

Ensuite, le contexte d'alignement du bloc d'ordonnancement commence par char qui représente le type de contexte pris en charge, suivi du bloc SEQUENCE qui représente les paramètres associés au contexte d'alignement AlignmentKind._context_params. Le type de contexte char est associé à chaque sous-classe d'alignement comme suit :

  • l: AlignLeft
  • r: AlignRight
  • s: AlignSequential
  • e: AlignEquispaced

Notez que le contexte AlignFunc n'est pas pris en charge en raison de la fonction de rappel stockée dans les paramètres du contexte.

PROGRAMME_BLOC_INSTRUCTIONS

Ce bloc d'alignement est suivi de num_element éléments de bloc qui peuvent consister en des blocs de programmation imbriqués et des instructions de programmation. Chaque instruction de programmation commence par char qui représente le type d'instruction, suivi du bloc SEQUENCE qui représente l'instruction operands. Notez que la structure de données de l'impulsion Instruction est unifiée de sorte que l'instance peut être déterminée de manière unique par la classe et un tuple d'opérandes. La correspondance entre le type char et la sous-classe d'instruction est définie comme suit :

  • a: Acquire instruction
  • p: Play instruction
  • d: Delay instruction
  • f: SetFrequency instruction
  • g: ShiftFrequency instruction
  • q: SetPhase instruction
  • r: ShiftPhase instruction
  • b: RelativeBarrier instruction
  • t: TimeBlockade instruction
  • yle programme de formation est disponible à l'adresse suivante : Reference instruction (nouveau dans la version 0.7 )

SCHEDULE_BLOCK_OPERANDS

Les opérandes de ces instances peuvent être sérialisés à l'aide du mécanisme standard de sérialisation des valeurs QPY, mais il existe des types d'objets spéciaux qui n'apparaissent que dans les opérandes du calendrier. Les opérandes étant sérialisés sous forme de SEQUENCE, chaque élément doit être emballé avec la structure d'emballage INSTRUCTION_PARAM, où chaque charge utile commence par un bloc d'en-tête composé des caractères type et uint64_t size. Les objets spéciaux commencent par la clé de type suivante :

  • c: Channel
  • w: Waveform
  • s: SymbolicPulse
  • o: string (operand string, nouveau dans la version 0.7 )

Canal

Le bloc de canaux commence par le sous-type de canal char qui associe les données d'un objet à la sous-classe Channel . La cartographie est définie comme suit :

  • d: DriveChannel
  • c : ControlChannel
  • m: MeasureChannel
  • a: AcquireChannel
  • e: MemorySlot
  • r: RegisterSlot

La clé est immédiatement suivie de l'index du canal sérialisé sous la forme d'un INSTRUCTION_PARAM.

Forme d'onde

Le bloc de forme d'onde commence par l'en-tête WAVEFORM :

struct {
    double epsilon;
    uint32_t data_size;
    _bool amp_limited;
}

qui est suivi par data_size octets de binaire complexe ndarray généré par numpy.save. Cela représente les points de données complexes du QI joués sur un dispositif quantique. name est sauvegardé après les échantillons dans la structure INSTRUCTION_PARAM pack, qui peut être une chaîne de caractères ou None.

SymbolicPulse

SymbolicPulse commence par l'en-tête SYMBOLIC_PULSE :

struct {
    uint16_t type_size;
    uint16_t envelope_size;
    uint16_t constraints_size;
    uint16_t valid_amp_conditions_size;
    _bool amp_limited;
}

qui est suivi par type_size utf8 octets de la chaîne SymbolicPulse.pulse_type qui représente une classe de forme d'onde, telle que "gaussienne" ou “GaussianSquare”. Ensuite, envelope_size, constraints_size, valid_amp_conditions_size utf8 octets d'expressions symboliques sérialisées sont générés pour SymbolicPulse.envelope, SymbolicPulse.constraints, et SymbolicPulse.valid_amp_conditions, respectivement. La représentation en chaîne de ces expressions étant généralement longue, l'expression binaire est générée par le module python zlib avec compression des données.

Pour spécifier de manière unique une instance d'impulsion, nous devons également stocker les paramètres associés, qui consistent en duration et le reste des paramètres sous la forme d'un dictionnaire. Les paramètres du dictionnaire sont tout d'abord transférés sous la forme MAPPING, puis duration est transféré avec la structure INSTRUCTION_PARAM. Enfin, name est également sauvegardé avec le pack struct INSTRUCTION_PARAM, qui peut être une chaîne de caractères ou None.

Mappage

Le MAPPING est une représentation d'un objet de cartographie arbitraire. Il s'agit d'une SÉQUENCE de longueur fixe de paires clé-valeur représentées par la charge utile MAP_ITEM.

Un MAP_ITEM commence par un en-tête défini comme suit :

struct {
    uint16_t key_size;
    char type;
    uint16_t size;
}

qui est immédiatement suivi par les octets key_size utf8 représentant la clé du dictionnaire en chaîne et size utf8 octets de données d'objets arbitraires de QPY sérialisables type.

CALIBRATIONS_DU_CIRCUIT

Le bloc CIRCUIT_CALIBRATIONS est un dictionnaire qui permet de définir les calibrations d'impulsions du jeu d'instructions personnalisé. Ce bloc commence par l'en-tête CALIBRATION suivant :

struct {
    uint16_t num_cals;
}

qui est suivie par la longueur num_cals des entrées d'étalonnage, chacune commençant par l'en-tête CALIBRATION_DEF :

struct {
    uint16_t name_size;
    uint16_t num_qubits;
    uint16_t num_params;
    char type;
}

L'en-tête de définition de l'étalonnage est ensuite suivi par name_size utf8 octets du nom de la porte, num_qubits longueur des entiers représentant une séquence de qubits, et num_params longueur de la charge utile INSTRUCTION_PARAM pour les paramètres associés à l'instruction personnalisée. Le type indique la classe du programme d'impulsions qui est, en principe, soit ScheduleBlock soit Schedule. Depuis la version 5 de QPY, seule la charge utile ScheduleBlock est prise en charge. Enfin, la charge utile SCHEDULE_BLOCK est emballée pour chaque entrée CALIBRATION_DEF.

Instruction

Le bloc INSTRUCTION a été modifié afin d'ajouter deux nouveaux champs num_ctrl_qubits et ctrl_state qui servent à modéliser les ControlledGate.num_ctrl_qubits attributs et ControlledGate.ctrl_state . Le nouveau format de la structure « payload packed » est le suivant :

struct {
    uint16_t name_size;
    uint16_t label_size;
    uint16_t num_parameters;
    uint32_t num_qargs;
    uint32_t num_cargs;
    _Bool has_conditional;
    uint16_t conditional_reg_name_size;
    int64_t conditional_value;
    uint32_t num_ctrl_qubits;
    uint32_t ctrl_state;
}

Le reste de l'instruction est identique. Vous pouvez vous référer aux INSTRUCTIONS pour les détails de la charge utile complète.

INSTRUCTION_PERSONNALISÉE

Le bloc CUSTOM_INSTRUCTION de la version 5 de QPY ajoute un nouveau champ base_gate_size qui sert à définir la taille de l'objet qiskit.circuit.Instruction stocké dans l'attribut ControlledGate.base_gate d'un objet personnalisé ControlledGate . Avec cette modification, le bloc de métadonnées CUSTOM_INSTRUCTION devient :

struct {
    uint16_t name_size;
    char type;
    uint32_t num_qubits;
    uint32_t num_clbits;
    _Bool custom_definition;
    uint64_t size;
    uint32_t num_ctrl_qubits;
    uint32_t ctrl_state;
    uint64_t base_gate_size
}

Immédiatement après la structure CUSTOM_INSTRUCTION se trouve le nom codé utf8 de taille name_size.

Si custom_definition est True , cela signifie que les octets qui suivent size immédiatement contiennent des données de circuit QPY pouvant être utilisées pour la définition personnalisée de cette porte. Si custom_definition est False alors l'instruction peut être considérée comme opaque (c'est-à-dire sans définition). Ce type champ détermine le type d'objet qui sera créé à partir de la définition personnalisée. Si c'est 'g' , ce sera un Gate objet; 'i' si c'est, ce sera un Instruction objet.

Ensuite, les octets base_gate_size suivants contiennent la charge utile INSTRUCTION pour ControlledGate.base_gate.

ControlledGateDe plus, une valeur supplémentaire est ajoutée 'c' à type ; celle-ci sert à indiquer que l'instruction personnalisée est une instruction personnalisée.

Version 4

La version 4 est identique à la version 3 sauf qu'elle ajoute 2 nouvelles chaînes de type à la structure INSTRUCTION_PARAM, z pour représenter None (qui est encodé comme aucune donnée), q pour représenter un circuit QPY (qui est encodé comme un circuit QPY), et pour représenter un d'entiers (qui est encodé comme un RANGE) QuantumCircuit (qui est encodé comme un circuit QPY), r pour représenter un range d'entiers (qui est encodé comme un RANGE ), et t pour représenter un sequence (qui est encodé comme défini par SEQUENCE ). En outre, la version 4 modifie le type de tableau de mappage d'index de registre, qui passe de uint32_t à int64_t. Si les valeurs de l'un des éléments du tableau sont négatives, elles représentent un bit de registre qui n'est pas présent dans le circuit.

Le format de l'en-tête REGISTERS a également été mis à jour pour

struct {
    char type;
    _Bool standalone;
    uint32_t size;
    uint16_t name_size;
    _bool in_circuit;
}

qui ajoute simplement le champ in_circuit qui indique si le registre fait partie du circuit ou non.

RANGE

Une GAMME est une représentation d'un objet range . Il est défini comme suit :

struct {
    int64_t start;
    int64_t stop;
    int64_t step;
}

Séquence

Une SÉQUENCE est une représentation d'un objet séquentiel arbitraire. Comme les séquences sont juste des conteneurs de longueur fixe d'objets python arbitraires, leur QPY ne peut pas représenter complètement n'importe quelle séquence, mais tant que le contenu d'une séquence est d'autres types sérialisables en QPY pour la charge utile INSTRUCTION_PARAM, l'objet séquence peut être sérialisé.

Un paramètre d'instruction de séquence commence par un en-tête défini comme suit :

struct {
    uint64_t size;
}

suivi de size éléments qui sont des charges utiles INSTRUCTION_PARAM, chacun d'entre eux définissant un élément de la séquence. L'objet séquence sera transformé en un type approprié, par exemple tuple, par la suite.

Version 3

La version 3 du format QPY est identique à la version 2, à l'exception qu'elle définit un format de structure permettant de représenter un PauliEvolutionGate de manière native dans QPY. Pour ce faire, la structure CUSTOM_DEFINITIONS prend désormais en charge une nouvelle valeur 'p' de type permettant de représenter un PauliEvolutionGate. Les entrées des tables d'instructions personnalisées se voient attribuer un nom unique, composé d'une chaîne de caractères "###PauliEvolutionGate_" suivie d'une chaîne UUID. Ce nom de porte est réservé dans QPY; si vous disposez d'un objet personnalisé Instruction comportant un ensemble de définitions et commençant par ce préfixe, une erreur se produira. S'il s'agit d'un type 'p' , la charge utile est définie comme suit :

PAULI_ÉVOLUTION

Cela représente le niveau élevé PauliEvolutionGate

struct {
    uint64_t operator_count;
    _Bool standalone_op;
    char time_type;
    uint64_t time_size;
    uint64_t synthesis_size;
}

Ces éléments sont immédiatement suivis par operator_count ceux définis par la charge utile SPARSE_PAULI_OP_LIST_ELEM. Viennent time_size ensuite les octets correspondant à time l'attribut. Si standalone_op est True alors il ne peut y avoir qu'un seul opérateur. Le codage de ces octets est déterminé par la valeur de time_type. Les valeurs possibles de time_type sont 'f', 'p', et 'e'. Si time_type est 'f' un double, 'p' définit un Parameter objet représenté par un PARAMETER, e définit un ParameterExpression objet (qui n'est pas un Parameter) représenté par un PARAMETER_EXPR. Vient synthesis_size ensuite « bytes », qui correspond à une charge utile JSON encodée au format « utf8 », représentant la EvolutionSynthesis classe utilisée par la passerelle.

SPARSE_PAULI_OP_LIST_ELEM

Il s'agit là d'un exemple de SparsePauliOp.

struct {
    uint32_t pauli_op_size;
}

qui est immédiatement suivi de pauli_op_size octets contenant des données au format.npy [2] représentant le SparsePauliOp.

La version 3 du format QPY définit également un format de structure permettant de représenter un ParameterVectorElement en tant que sous-classe distincte d'un Parameter. Cela ajoute un nouveau type de paramètre « char 'v' » pour représenter une ParameterVectorElement valeur de type chaîne de caractères, désormais prise en charge pour un INSTRUCTION_PARAM. Les données utiles associées à ces paramètres sont définies ci-dessous sous le nom PARAMETER_VECTOR_ELEMENT.

PARAMÈTRE_VECTEUR_ÉLÉMENT

Un PARAMETER_VECTOR_ELEMENT représente un ParameterVectorElement objet contenant les données d'un INSTRUCTION_PARAM. Le contenu de PARAMETER_VECTOR_ELEMENT est défini comme suit :

struct {
    uint16_t vector_name_size;
    uint64_t vector_size;
    char uuid[16];
    uint64_t index;
}

qui est immédiatement suivi de vector_name_size utf8 octets représentant le nom du vecteur du paramètre.

PARAMÈTRE_EXPR

De plus, étant donné que la version v3 du format QPY fait la distinction entre un Parameter et ParameterVectorElement un, la charge utile d'un ParameterExpression doit être mise à jour afin de distinguer ces deux types. Voici le format de charge utile modifié, qui est pratiquement identique à celui des versions 1 et 2, à la seule différence que la map_elements structure a été modifiée pour inclure un champ de type « symbole ».

Un PARAMETER_EXPR représente un ParameterExpression objet contenant les données d'un INSTRUCTION_PARAM. Le contenu d'un PARAMETER_EXPR est défini comme suit :

struct {
    uint64_t map_elements;
    uint64_t expr_size;
}

Immédiatement après l'en-tête se trouve expr_size octets de données utf8 contenant la chaîne d'expression, qui est le srepr sympy de l'expression pour l'expression du paramètre. Vient ensuite une carte de symboles qui contient map_elements éléments au format

struct {
    char symbol_type;
    char type;
    uint64_t size;
}

La symbol_type clé détermine le type de charge utile de la représentation symbolique de l'élément. Si c'est p , cela représente un Parameter et si c'est v , cela représente un ParameterVectorElement. La structure de l'élément « map » est immédiatement suivie de la charge utile de la clé « symbol map »; si symbol_type est p alors elle est immédiatement suivie d'un objet PARAMETER (à la fois la structure et les octets de nom « utf8 ») et si symbol_type est v alors la structure est immédiatement suivie de PARAMETER_VECTOR_ELEMENT (à la fois la structure et les octets de nom « utf8 »). Viennent ensuite size les octets correspondant aux données du symbole. Le format des données dépend de la valeur de type. Si type est p alors il s'agit d'un Parameter et la taille sera égale à 0; la valeur sera simplement identique à la clé. De même, si est v``type alors il s'agit d'un ParameterVectorElement et la taille sera égale à 0, car la valeur sera identique à la clé. Si type est f , alors il s'agit d'un nombre à virgule flottante en double précision. Si type est c , cela correspond à un nombre complexe en double précision, représenté par le type COMPLEX. Enfin, si le type est i , cela représente un entier qui est un int64_t.

Version 2

La version 2 du format QPY est identique à la version 1, à l'exception de la section HEADER qui est légèrement différente. Vous pouvez vous référer à la section Version 1 pour plus de détails sur le reste du format de la charge utile.

EN-TÊTE

Le contenu de HEADER est défini comme une structure C are :

struct {
    uint16_t name_size;
    char global_phase_type;
    uint16_t global_phase_size;
    uint32_t num_qubits;
    uint32_t num_clbits;
    uint64_t metadata_size;
    uint32_t num_registers;
    uint64_t num_instructions;
}

Cette information est immédiatement suivie de name_size octets de données de type « utf8 » correspondant au nom du circuit. Viennent ensuite immédiatement global_phase_size les octets représentant la phase globale. Le contenu de ces données dépend de la valeur de global_phase_type. Si la donnée est 'f' de type float et a la taille d'un double. Si 'p' définit un Parameter objet représenté par une structure PARAM (voir ci-dessous), e définit un ParameterExpression objet (qui n'est pas un Parameter) représenté par une structure PARAM_EXPR (voir ci-dessous).

Version 1

EN-TÊTE

Le contenu de HEADER, tel qu'il est défini dans une structure C, est le suivant :

struct {
    uint16_t name_size;
    double global_phase;
    uint32_t num_qubits;
    uint32_t num_clbits;
    uint64_t metadata_size;
    uint32_t num_registers;
    uint64_t num_instructions;
}

Il est immédiatement suivi de name_size octets de données utf8 pour le nom du circuit.

Métadonnées

Le champ METADATA est une chaîne JSON codée UTF8. Après avoir lu le HEADER (qui a une taille fixe au début du fichier QPY) et la chaîne name , vous lisez ensuite le nombre d'octets de metadata_size et analysez le JSON pour obtenir les métadonnées du circuit.

Registres

Le contenu des REGISTRES est un numéro d'objet REGISTRE. Si num_registers est > 0, après avoir lu les METADATA, vous lisez le nombre de REGISTER structs définis comme suit :

struct {
    char type;
    _Bool standalone;
    uint32_t size;
    uint16_t name_size;
}

type peut être 'q' ou 'c'.

Immédiatement après la structure REGISTER se trouve le nom du registre encodé utf8 de taille name_size. Après les octets name utf8, il y a un tableau de valeurs int64_t de taille size qui contient une correspondance entre l'index du registre et l'index du qubit du circuit. Par exemple, la valeur de l'élément de tableau 0’s est l'indice de la position de register[0]dans la liste des qubits du circuit contenant.

Remarque

Avant la version 4 de QPY, le type des éléments du tableau était uint32_t. Ceci a été modifié pour permettre des valeurs négatives qui représentent les bits du tableau qui ne sont pas présents dans le circuit

Le booléen autonome détermine si le registre est construit comme un registre autonome ajouté au circuit ou créé à partir de bits existants. Un registre est considéré comme autonome s'il comporte des bits construits uniquement dans le cadre de ce registre, par exemple :

qr = QuantumRegister(2)
qc = QuantumCircuit(qr)

le registre qr serait un registre autonome. Alors que quelque chose comme :

bits = [Qubit(), Qubit()]
qr2 = QuantumRegister(bits=bits)
qc = QuantumCircuit(qr2)

qr2 aurait pour conséquence que standalone serait réglé sur False.

DÉFINITIONS PERSONNALISÉES

Cette section spécifie des définitions personnalisées pour n'importe quelle instruction du circuit.

Le contenu de CUSTOM_DEFINITION_HEADER est défini comme suit :

struct {
    uint64_t size;
}

Si la taille est supérieure à 0, cela signifie que le circuit contient des instructions personnalisées. Chaque instruction personnalisée est définie par un bloc CUSTOM_INSTRUCTION défini comme suit :

struct {
    uint16_t name_size;
    char type;
    uint32_t num_qubits;
    uint32_t num_clbits;
    _Bool custom_definition;
    uint64_t size;
}

Immédiatement après la structure CUSTOM_INSTRUCTION se trouve le nom codé utf8 de taille name_size.

Si custom_definition est True , cela signifie que les octets qui suivent size immédiatement contiennent des données de circuit QPY pouvant être utilisées pour la définition personnalisée de cette porte. Si custom_definition est False alors l'instruction peut être considérée comme opaque (c'est-à-dire sans définition). Ce type champ détermine le type d'objet qui sera créé à partir de la définition personnalisée. Si c'est 'g' , ce sera un Gate objet; 'i' si c'est, ce sera un Instruction objet.

INSTRUCTIONS

Le contenu de INSTRUCTIONS est une liste d'objets de métadonnées INSTRUCTION

struct {
    uint16_t name_size;
    uint16_t label_size;
    uint16_t num_parameters;
    uint32_t num_qargs;
    uint32_t num_cargs;
    _Bool has_conditional;
    uint16_t conditional_reg_name_size;
    int64_t conditional_value;
}

Cet objet de métadonnées est immédiatement suivi de name_size octets de utf8 octets pour name. name voici le nom de la classe Qiskit pour la classe Instruction si elle est définie dans Qiskit. Dans le cas contraire, le nom de l'instruction personnalisée est utilisé. Après les octets name , il y a des octets label_size de données utf8 pour l'étiquette si celle-ci a été définie dans l'instruction. Après les octets d'étiquette, si has_conditional est True , il y a conditional_reg_name_size octets de données utf8 pour le nom du registre conditionnel. Dans le cas de conditions relatives à un seul bit classique, le nom du registre utf8 sera précédé d'un caractère nul “x00” et d'un entier de la chaîne utf8 représentant l'indice du bit classique dans le circuit concerné par la condition.

Elle est immédiatement suivie par les structs INSTRUCTION_ARG pour la liste des arguments de cette instruction. Ils sont classés dans l'ordre suivant : tous les arguments quantiques (il y en a un certain nombre), puis tous les arguments classiques (il y en a un certain nombre).

Le contenu de chaque INSTRUCTION_ARG est le suivant :

struct {
    char type;
    uint32_t index;
}

type peut être 'q' ou 'c'.

Après tous les arguments d'une instruction, les paramètres sont spécifiés à l'aide de la structure num_parameters INSTRUCTION_PARAM.

Le contenu de chaque INSTRUCTION_PARAM est le suivant :

struct {
    char type;
    uint64_t size;
}

Après chaque INSTRUCTION_PARAM, les octets suivants size correspondent aux données du paramètre. Le type champ peut être 'i', 'f', 'p', 'e', 's', 'c' ou 'n' ; ce sont ces éléments qui déterminent le format. S'il 'i' s'agit d'un entier, 'f' d'un nombre à virgule flottante de type double, 's' d'une chaîne de caractères (encodée sous la forme utf8 ), 'c' ou d'un nombre complexe, les données sont représentées selon le format de structure défini dans la section PARAMETER_EXPR. 'p' définit un Parameter objet représenté par une structure PARAMETER, e définit un ParameterExpression objet (qui n'est pas un Parameter) représenté par une structure PARAMETER_EXPR (dans la version 3 du format QPY, le format est légèrement modifié, voir : PARAMETER_EXPR ), 'n' représente un objet issu de numpy (soit un ndarray soit un type numpy), ce qui signifie que les données sont au format.npy [2], et dans la version 3 'v' de QPY, représente un ParameterVectorElement qui est représenté par une structure PARAMETER_VECTOR_ELEMENT.

Paramètre

Un PARAMÈTRE représente un Parameter objet contenant les données d'une INSTRUCTION_PARAM. Le contenu du PARAMÈTRE est défini comme suit :

struct {
    uint16_t name_size;
    char uuid[16];
}

qui est immédiatement suivi de name_size utf8 octets représentant le nom du paramètre.

PARAMÈTRE_EXPR

Un PARAMETER_EXPR représente un ParameterExpression objet contenant les données d'un INSTRUCTION_PARAM. Le contenu d'un PARAMETER_EXPR est défini comme suit :

Les données de PARAMETER_EXPR commencent par un en-tête :

struct {
    uint64_t map_elements;
    uint64_t expr_size;
}

Immédiatement après l'en-tête se trouve expr_size octets de données utf8 contenant la chaîne d'expression, qui est le srepr sympy de l'expression pour l'expression du paramètre. Vient ensuite une carte de symboles qui contient map_elements éléments au format

struct {
    char type;
    uint64_t size;
}

Ce qui est immédiatement suivi de PARAMETER l'objet (à la fois la structure et les octets de nom de utf8 ) correspondant à la clé de la table des symboles. Viennent ensuite size les octets correspondant aux données du symbole. Le format des données dépend de la valeur de type. Si type est p alors il s'agit d'un Parameter et la taille sera égale à 0; la valeur sera simplement identique à la clé. Si type est f , alors il s'agit d'un nombre à virgule flottante en double précision. Si type est c , cela correspond à un nombre complexe en double précision, représenté par COMPLEX. Enfin, si le type est i , cela représente un entier qui est un int64_t.

Complexe

Lors de la représentation d'une valeur complexe de double précision en QPY, la structure suivante est utilisée :

struct {
    double real;
    double imag;
}

cela correspond à la représentation interne en C du type complexe de Python. [3]


Références

[1 ]

https://tools.ietf.org/html/rfc1700

[2] (1,2 )

https://numpy.org/doc/stable/reference/generated/numpy.lib.format.html

[3 ]

https://docs.python.org/3/c-api/complex.html# c.Py_complex

Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.