-
Notifications
You must be signed in to change notification settings - Fork 127
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
HEAT experiments #625
base: main
Are you sure you want to change the base?
HEAT experiments #625
Changes from 41 commits
7b2bfc1
fccf94c
6e64321
49a7845
b766f56
d32444f
c0e0da5
423dad4
8adcca2
942e558
4f70f6e
9cc247f
e8a11f7
00a991a
7c49da1
4a3c645
ca871ac
8ced3c2
52a1439
42f0768
0cec011
16fb9ea
6175036
532f855
e379b49
c7318b8
9a59715
ef8c56c
30f9200
c4333fb
6524858
cf47445
077d217
a773faa
9819c16
678af55
1ceff09
e540446
e103640
d918470
280cae1
4f7cd81
5c40d7d
4882e9e
a62cc8f
5d4945e
c908105
6c1a556
995a009
caf945a
c400186
aba80ca
fec3997
1e21e36
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -30,3 +30,4 @@ Experiment Modules | |
mod_randomized_benchmarking | ||
mod_tomography | ||
mod_quantum_volume | ||
mod_hamiltonian |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
.. _qiskit-experiments-hamiltonian: | ||
|
||
.. automodule:: qiskit_experiments.library.hamiltonian | ||
:no-members: | ||
:no-inherited-members: | ||
:no-special-members: |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -13,8 +13,9 @@ | |
Composite Experiment abstract base class. | ||
""" | ||
|
||
from typing import List, Sequence, Optional, Union | ||
from typing import List, Sequence, Optional, Union, Type | ||
from abc import abstractmethod | ||
import functools | ||
import warnings | ||
from qiskit.providers.backend import Backend | ||
from qiskit_experiments.framework import BaseExperiment, ExperimentData | ||
|
@@ -131,3 +132,75 @@ def _postprocess_transpiled_circuits(self, circuits, **run_options): | |
for expr in self._experiments: | ||
if not isinstance(expr, CompositeExperiment): | ||
expr._postprocess_transpiled_circuits(circuits, **run_options) | ||
|
||
|
||
def sync_transpile_options( | ||
composite_cls: Type[CompositeExperiment], | ||
) -> Type[CompositeExperiment]: | ||
"""A class decorator that overrides the transpile option setter method. | ||
|
||
This method overrides the behavior of :meth:`set_transpile_options` method. | ||
The option values set to the composite instance | ||
will be propagated through all component experiments. | ||
|
||
Args: | ||
composite_cls: CompositeExperiment subclass to decorate. | ||
|
||
Returns: | ||
Composite experiment that implements option synchronization. | ||
|
||
Raises: | ||
TypeError: When class is not subclass of :class:`CompositeExperiment` | ||
""" | ||
if not issubclass(composite_cls, CompositeExperiment): | ||
raise TypeError("Class is not composite experiment. Cannot override method.") | ||
|
||
options_setter = getattr(composite_cls, "set_transpile_options") | ||
|
||
@functools.wraps(options_setter) | ||
def sync_opts(instance, **fields): | ||
options_setter(instance, **fields) | ||
# set the same options to component experiments | ||
for comp in instance.component_experiment(): | ||
comp.set_transpile_options(**fields) | ||
|
||
# override set method | ||
setattr(composite_cls, "set_transpile_options", sync_opts) | ||
|
||
return composite_cls | ||
|
||
|
||
def sync_experiment_options( | ||
composite_cls: Type[CompositeExperiment], | ||
) -> Type[CompositeExperiment]: | ||
Comment on lines
+182
to
+184
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Same comment as above. Using decorators here only makes the code harder to follow. |
||
"""A class decorator that overrides the experiment option setter method. | ||
|
||
This method overrides the behavior of :meth:`set_experiment_options` method. | ||
The option values set to the composite instance | ||
will be propagated through all component experiments. | ||
|
||
Args: | ||
composite_cls: CompositeExperiment subclass to decorate. | ||
|
||
Returns: | ||
Composite experiment that implements option synchronization. | ||
|
||
Raises: | ||
TypeError: When class is not subclass of :class:`CompositeExperiment` | ||
""" | ||
if not issubclass(composite_cls, CompositeExperiment): | ||
raise TypeError("Class is not composite experiment. Cannot override method.") | ||
|
||
options_setter = getattr(composite_cls, "set_experiment_options") | ||
|
||
@functools.wraps(options_setter) | ||
def sync_opts(instance, **fields): | ||
options_setter(instance, **fields) | ||
# set the same options to component experiments | ||
for comp in instance.component_experiment(): | ||
comp.set_experiment_options(**fields) | ||
|
||
# override set method | ||
setattr(composite_cls, "set_experiment_options", sync_opts) | ||
|
||
return composite_cls |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,65 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2022. | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
""" | ||
=============================================================================================== | ||
Hamiltonian Characterization Experiments (:mod:`qiskit_experiments.library.hamiltonian`) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is a fairly broad title. Should the contents in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably (edit) |
||
=============================================================================================== | ||
|
||
.. currentmodule:: qiskit_experiments.library.hamiltonian | ||
|
||
This module provides a set of experiments to characterize qubit Hamiltonians. | ||
|
||
HEAT Experiments | ||
================ | ||
|
||
HEAT stands for `Hamiltonian Error Amplifying Tomography` which amplifies the | ||
dynamics of an entangling gate along a specified axis of the target qubit. Here, | ||
errors are typically amplified by repeating a sequence of gates which results in | ||
a ping-pong pattern when measuring the qubit population. | ||
|
||
HEAT for ZX Hamiltonian | ||
----------------------- | ||
eggerdj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
.. autosummary:: | ||
:toctree: ../stubs/ | ||
:template: autosummary/experiment.rst | ||
|
||
ZXHeat | ||
ZX90HeatXError | ||
ZX90HeatYError | ||
ZX90HeatZError | ||
|
||
HEAT Analysis | ||
------------- | ||
|
||
.. autosummary:: | ||
:toctree: ../stubs/ | ||
:template: autosummary/analysis.rst | ||
|
||
HeatElementAnalysis | ||
HeatAnalysis | ||
|
||
HEAT Base Classes | ||
----------------- | ||
|
||
.. autosummary:: | ||
:toctree: ../stubs/ | ||
:template: autosummary/experiment.rst | ||
|
||
HeatElement | ||
|
||
|
||
""" | ||
|
||
from .heat_base import HeatElement | ||
from .heat_zx import ZXHeat, ZX90HeatXError, ZX90HeatYError, ZX90HeatZError | ||
from .heat_analysis import HeatElementAnalysis, HeatAnalysis |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,157 @@ | ||
# This code is part of Qiskit. | ||
# | ||
# (C) Copyright IBM 2021. | ||
# | ||
# This code is licensed under the Apache License, Version 2.0. You may | ||
# obtain a copy of this license in the LICENSE.txt file in the root directory | ||
# of this source tree or at http://www.apache.org/licenses/LICENSE-2.0. | ||
# | ||
# Any modifications or derivative works of this code must retain this | ||
# copyright notice, and modified files need to carry a notice indicating | ||
# that they have been altered from the originals. | ||
""" | ||
Analysis for HEAT experiments. | ||
""" | ||
|
||
from typing import Tuple | ||
|
||
import numpy as np | ||
|
||
from qiskit_experiments.curve_analysis import ErrorAmplificationAnalysis, ParameterRepr | ||
from qiskit_experiments.exceptions import AnalysisError | ||
from qiskit_experiments.framework import ( | ||
CompositeAnalysis, | ||
ExperimentData, | ||
AnalysisResultData, | ||
Options, | ||
FitVal, | ||
) | ||
|
||
|
||
class HeatElementAnalysis(ErrorAmplificationAnalysis): | ||
"""An analysis class for HEAT experiment to define the fixed parameters. | ||
|
||
# section: overview | ||
|
||
This is standard error amplification analysis. | ||
|
||
# section: see_also | ||
qiskit_experiments.curve_analysis.ErrorAmplificationAnalysis | ||
""" | ||
|
||
__fixed_parameters__ = ["angle_per_gate", "phase_offset", "amp"] | ||
|
||
@classmethod | ||
def _default_options(cls) -> Options: | ||
"""Default analysis options.""" | ||
options = super()._default_options() | ||
options.angle_per_gate = np.pi | ||
options.phase_offset = np.pi / 2 | ||
options.amp = 1.0 | ||
|
||
return options | ||
|
||
|
||
class HeatAnalysis(CompositeAnalysis): | ||
r"""A composite error amplification analysis to get unitary error coefficients. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think this docstring would be benefit from writting down the Hamiltonian so that we can better see how the terms There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The model is written in the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not write an example model and make it clear that this has a broader use. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Any example of the model? (without assuming specific amplification sequence) I can write a model specific to for example ZX Hamiltonian but this may mislead user so that usecase is limited. |
||
|
||
# section: fit_model | ||
|
||
Heat experiment amplifies the dynamics of the entangling gate along the | ||
experiment-specific error axis on the target qubit Bloch sphere. | ||
This analysis takes two error amplification experiment results performed with | ||
different states of the control qubit to distinguish the contribution | ||
of local term (such as IX) from non-local term (such as ZX). | ||
|
||
This analysis takes a set of `d_theta` parameters from child error amplification results | ||
which might be represented by a unique name in the child experiment data. | ||
With these fit parameters, two Hamiltonian coefficients will be computed as | ||
|
||
.. math:: | ||
|
||
A_{I\beta} = \frac{1}{2}\left( d\theta_{\beta 0} + d\theta_{\beta 1} \right) \\ | ||
|
||
A_{Z\beta} = \frac{1}{2}\left( d\theta_{\beta 0} - d\theta_{\beta 1} \right) | ||
|
||
where, :math:`\beta \in [X, Y, Z]` is a single-qubit Pauli term, and | ||
:math:`d\theta_{\beta k}` is an angle error ``d_theta`` extracted from the HEAT experiment | ||
with the control qubit in state :math:`|k\rangle \in [|0\rangle, |1\rangle]`. | ||
|
||
# section: see_also | ||
qiskit_experiments.library.hamiltonian.HeatElementAnalysis | ||
nkanazawa1989 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
qiskit_experiments.curve_analysis.ErrorAmplificationAnalysis | ||
|
||
""" | ||
|
||
def __init__( | ||
self, | ||
fit_params: Tuple[str, str], | ||
out_params: Tuple[str, str], | ||
): | ||
"""Create new HEAT analysis. | ||
|
||
Args: | ||
fit_params: Name of error parameters for each amplification sequence. | ||
out_params: Name of Hamiltonian coefficients. | ||
|
||
Raises: | ||
AnalysisError: When size of ``fit_params`` or ``out_params`` are not 2. | ||
nkanazawa1989 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
""" | ||
if len(fit_params) != 2: | ||
raise AnalysisError( | ||
f"{self.__class__.__name__} assumes two fit parameters extracted from " | ||
"a set of experiments with different control qubit state input. " | ||
f"{len(fit_params)} input parameter names are specified." | ||
) | ||
|
||
if len(out_params) != 2: | ||
raise AnalysisError( | ||
f"{self.__class__.__name__} assumes two output parameters computed with " | ||
"a set of experiment results with different control qubit state input. " | ||
f"{len(out_params)} output parameter names are specified." | ||
) | ||
|
||
analyses = [] | ||
for fit_parm in fit_params: | ||
sub_analysis = HeatElementAnalysis() | ||
sub_analysis.set_options(result_parameters=[ParameterRepr("d_theta", fit_parm, "rad")]) | ||
analyses.append(sub_analysis) | ||
|
||
super().__init__(analyses=analyses) | ||
|
||
self._fit_params = fit_params | ||
self._out_params = out_params | ||
|
||
def _run_analysis(self, experiment_data: ExperimentData): | ||
|
||
# wait for child experiments to complete | ||
super()._run_analysis(experiment_data) | ||
eggerdj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
# extract d_theta parameters | ||
fit_results = [] | ||
for i, pname in enumerate(self._fit_params): | ||
fit_results.append(experiment_data.child_data(i).analysis_results(pname)) | ||
|
||
# Check data quality | ||
is_good_quality = all(r.quality == "good" for r in fit_results) | ||
|
||
# Compute unitary terms | ||
ib = (fit_results[0].value.value + fit_results[1].value.value) / 2 | ||
zb = (fit_results[0].value.value - fit_results[1].value.value) / 2 | ||
|
||
# Compute new variance | ||
sigma = np.sqrt(fit_results[0].value.stderr ** 2 + fit_results[1].value.stderr ** 2) | ||
nkanazawa1989 marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
estimate_ib = AnalysisResultData( | ||
name=self._out_params[0], | ||
value=FitVal(value=ib, stderr=sigma, unit="rad"), | ||
quality="good" if is_good_quality else "bad", | ||
) | ||
|
||
estimate_zb = AnalysisResultData( | ||
name=self._out_params[1], | ||
value=FitVal(value=zb, stderr=sigma, unit="rad"), | ||
quality="good" if is_good_quality else "bad", | ||
) | ||
|
||
return [estimate_ib, estimate_zb], [] |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is this needed in the first place? I wonder if this should be the default behavior of
CompositeExperiment
? Or why not have this as an extra function in theCompositeExperiment
? I don't see why the decorator framework is needed when we only decorate a single method of a single class.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This aims at making a batch experiment to behave as if a single experiment (because user doesn't need to recognize its implementation to use). This SHOUD not be default behavior (unfortunately it is now) because we should be able to transpile batch (I believe also parallel) experiment with its own configuration.