TeNPy backend basé sur les couches
qiskit_addon_mpf.backends.tenpy_layers
Un backend à évolution temporelle par couches utilisant tenpy.
Ce backend n'est disponible que si les dépendances optionnelles ont été installées :
pip install "qiskit-addon-mpf[tenpy]"LayerwiseEvolver | Un cas particulier de la TEBDEvolver basé sur des modèles d'évolution en couches. |
LayerModel | Un modèle pour représenter une couche d'interactions d'évolution temporelle. |
Méthode sous-jacente
Ce module fournit un backend d'évolution temporelle similaire à celui basé sur le TEBD fourni par le module tenpy_tebd module. La principale différence réside dans le fait que ce module offre à l'utilisateur une flexibilité totale pour définir ses formules de produits, ne le limitant pas aux options intégrées dans la tenpy bibliothèque.
À la base, l'algorithme fourni par ce module est toujours un algorithme TEBD [1.] Cependant, plutôt que d'imposer des mises à jour alternées des liaisons paires et impaires de l'état d'évolution temporelle (voir également evolve_step()), cette implémentation confie la responsabilité de la mise à jour des liens en alternance à la définition de plusieurs couches d' évolution temporelle.
La meilleure façon de l'expliquer est de le faire à l'aide d'un exemple. Supposons que nous ayons un hamiltonien générique agissant sur une chaîne de sites à une dimension.
Ci-dessous, nous sommes très attentifs à l'ordre des termes de Pauli de l'hamiltonien, car cela a un impact direct sur la structure du circuit d'évolution temporelle.
>>> from qiskit.quantum_info import SparsePauliOp
>>> hamil = SparsePauliOp.from_sparse_list(
... [("ZZ", (i, i+1), 1.0) for i in range(1, 9, 2)] +
... [("Z", (i,), 0.5) for i in range(10)] +
... [("ZZ", (i, i+1), 1.0) for i in range(0, 9, 2)],
... num_qubits=10,
... )Examinons maintenant le circuit d'évolution temporelle de cet hamiltonien à l'aide d'une formule de Suzuki-Trotter du second ordre.
>>> from qiskit.synthesis import SuzukiTrotter
>>> from qiskit_addon_utils.problem_generators import generate_time_evolution_circuit
>>> circuit = generate_time_evolution_circuit(hamil, time=1.0, synthesis=SuzukiTrotter(order=2))
>>> circuit.draw("mpl")
<Figure size 956...x869... with 1 Axes>
Dans le circuit ci-dessus, nous pouvons clairement identifier sa structure en couches. Nous pouvons encore accentuer ce point en divisant le circuit en plusieurs couches comme indiqué ci-dessous (nous recombinons le site layers en un seul circuit avec des barrières entre elles pour faciliter la visualisation).
>>> from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
>>> layers = slice_by_gate_types(circuit)
>>> combine_slices(layers, include_barriers=True).draw("mpl")
<Figure size 1374...x869... with 1 Axes>
L'asymétrie des couches centrales résulte de la mise en œuvre de la formule de Qiskit SuzukiTrotter de Qiskit. Dans sa forme du second ordre, il combine les deux évolutions à mi-temps du dernier terme de l'hamiltonien en une seule d'une longueur deux fois supérieure. Nous pourrions transposer ce circuit pour réduire toutes les portes sous-équivalentes dans les deux couches centrales (comme dans l'exemple précédent), mais pour la simplicité de cet exemple, nous ne le ferons pas ici.
Il n'est pas possible d'instruire l'algorithme TeNPy’s TEBD pour simuler la structure exacte du circuit illustré ci-dessus. La raison en est une limitation de son interface, car il n'accepte que l'hamiltonien complet, qui est ensuite évolué dans le temps à l'aide de formules de Trotter prédéfinies. Toutefois, ce faisant, il ne donne aucune importance à l'ordre des termes de Pauli dans un hamiltonien (comme nous le faisons ici).
Si l'on souhaite calculer les coefficients MPF dynamiques d'une évolution temporelle utilisant une structure de formule de produit autre que celles implémentées dans TeNPy (comme dans l'exemple ci-dessus), on peut alors utiliser l'algorithme d'évolution temporelle fourni par ce module. Plutôt que de prendre un seul hamiltonien monolithique dont l'évolution temporelle doit être modélisée, l'application LayerwiseEvolver accepte une liste d'objets LayerModel chacun décrivant une couche individuelle de la formule du produit. L'utilisateur dispose ainsi d'une souplesse totale pour définir la décomposition de Trotter jusqu'au niveau le plus granulaire.
Toutefois, il convient de veiller à ce que la propriété de la TEBD de mettre à jour les obligations paires et impaires de manière alternée soit toujours garantie. Heureusement, pour les circuits quantiques constitués de portes de deux qubits au maximum, cette propriété est satisfaite par construction.
Exemple de code
Dans cette section, nous reprenons l'exemple ci-dessus et montrons comment prendre une formule de Trotter personnalisée et l'utiliser pour construire un LayerwiseEvolver qui peut être utilisée pour remplacer le tenpy_tebd.TEBDEvolver dans le flux de travail décrit dans tenpy_tebd.
Le déroulement général de l'utilisation de ce module est le même que celui du module tenpy_tebd il convient donc de lire également ces instructions.
En d'autres termes, nous devons convertir chacun des circuits layers (voir ci-dessus) en une LayerModel instance. Pour ce faire, nous pouvons utiliser sa méthode from_quantum_circuit() méthode.
>>> from qiskit_addon_mpf.backends.tenpy_layers import LayerModel
>>> model0 = LayerModel.from_quantum_circuit(layers[0])
>>> layer_models = [model0]Dans le code ci-dessus, vous pouvez voir à quel point la conversion est simple pour les couches qui ne contiennent que des portes à deux qubits agissant sur des qubits mutuellement exclusifs (ce que les couches de profondeur 1 garantissent).
Cependant, nous devons être plus prudents avec les couches comprenant des portes à qubit unique. La raison en est que l'algorithme TEBD sous-jacent à l'application LayerwiseEvolver doit mettre à jour les obligations paires et impaires en alternance. Et comme les portes à qubit unique ne sont pas appliquées sur un site, mais divisées en deux et appliquées aux liaisons de part et d'autre, une couche de portes à qubit unique agissant sur tous les qubits briserait cette hypothèse.
Pour contourner ce problème, nous pouvons prendre n'importe quelle couche composée uniquement de portes à un qubit et l'appliquer deux fois (une fois sur les liaisons paires et une fois sur les liaisons impaires).
>>> model1a = LayerModel.from_quantum_circuit(layers[1], keep_only_odd=True)
>>> model1b = LayerModel.from_quantum_circuit(layers[1], keep_only_odd=False)
>>> layer_models.extend([model1a, model1b])Maintenant que nous savons comment traiter les couches composées de portes à deux qubits et à un qubit, nous pouvons transformer les couches restantes.
>>> for layer in layers[2:]:
... num_qubits = len(layer.data[0].qubits)
... if num_qubits == 2:
... layer_models.append(LayerModel.from_quantum_circuit(layer))
... else:
... layer_models.append(
... LayerModel.from_quantum_circuit(layer, keep_only_odd=True)
... )
... layer_models.append(
... LayerModel.from_quantum_circuit(layer, keep_only_odd=False)
... )
>>> assert len(layer_models) == 8Au final, nous avons 8 LayerModelun pour chacune des 4 couches à deux qubits et deux pour chacune des 2 couches à un qubit.
Enfin, nous pouvons définir notre ApproxEvolverFactory à utiliser dans la fonction setup_dynamic_lse() fonction.
>>> from functools import partial
>>> from qiskit_addon_mpf.backends.tenpy_layers import LayerwiseEvolver
>>> approx_evolver_factory = partial(
... LayerwiseEvolver,
... layers=layer_models,
... options={
... "preserve_norm": False,
... "trunc_params": {
... "chi_max": 10,
... "svd_min": 1e-5,
... "trunc_cut": None,
... },
... },
... )Il convient de noter que, dans ce flux de travail, nous n'avons pas encore fixé le pas de temps utilisé par la formule de Trotter. Nous n'avons également mis en place qu'une seule répétition de la formule de Trotter, le reste étant effectué par l'algorithme interne DynamicMPF interne, exécuté pendant setup_dynamic_lse().
Bien entendu, vous pouvez également l'utiliser pour spécifier un fichier ExactEvolverFactory. Mais vous pouvez également combiner un tenpy_layers.LayerwiseEvolver avec un tenpy_tebd.TEBDEvolver.
Ressources
[1] : https://en.wikipedia.org/wiki/Time-evolving \Nblock\Ndecimation