diff --git a/docs/_ext/autodoc_analysis.py b/docs/_ext/autodoc_analysis.py
new file mode 100644
index 0000000000..73c0cac5a9
--- /dev/null
+++ b/docs/_ext/autodoc_analysis.py
@@ -0,0 +1,67 @@
+# 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.
+
+"""
+Documentation extension for analysis class.
+"""
+
+from typing import Any
+
+from docs._ext.custom_styles.styles import AnalysisDocstring
+from qiskit_experiments.framework.base_analysis import BaseAnalysis
+from sphinx.application import Sphinx
+from sphinx.ext.autodoc import ClassDocumenter
+
+
+class AnalysisDocumenter(ClassDocumenter):
+ """Sphinx extension for the custom documentation of the standard analysis class."""
+
+ objtype = "analysis"
+ directivetype = 'class'
+ priority = 10 + ClassDocumenter.priority
+ option_spec = dict(ClassDocumenter.option_spec)
+
+ @classmethod
+ def can_document_member(
+ cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
+ return isinstance(member, BaseAnalysis)
+
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
+ sourcename = self.get_sourcename()
+
+ # analysis class doesn't have explicit init method.
+ class_doc = self.get_doc()[0]
+
+ # format experiment documentation into the analysis style
+ class_doc_parser = AnalysisDocstring(
+ target_cls=self.object,
+ docstring_lines=class_doc,
+ config=self.env.app.config,
+ indent=self.content_indent,
+ )
+
+ # write introduction
+ for i, line in enumerate(self.process_doc(class_doc_parser.generate_class_docs())):
+ self.add_line(line, sourcename, i)
+ self.add_line("", sourcename)
+
+ # method and attributes
+ if more_content:
+ for line, src in zip(more_content.data, more_content.items):
+ self.add_line(line, src[0], src[1])
+
+
+def setup(app: Sphinx):
+ existing_documenter = app.registry.documenters.get(AnalysisDocumenter.objtype)
+ if existing_documenter is None or not issubclass(existing_documenter, AnalysisDocumenter):
+ app.add_autodocumenter(AnalysisDocumenter, override=True)
diff --git a/docs/_ext/autodoc_experiment.py b/docs/_ext/autodoc_experiment.py
new file mode 100644
index 0000000000..389854d881
--- /dev/null
+++ b/docs/_ext/autodoc_experiment.py
@@ -0,0 +1,80 @@
+# 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.
+
+"""
+Documentation extension for experiment class.
+"""
+
+from typing import Any
+
+from docs._ext.custom_styles.styles import ExperimentDocstring
+from qiskit.exceptions import QiskitError
+from qiskit_experiments.framework.base_experiment import BaseExperiment
+from sphinx.application import Sphinx
+from sphinx.ext.autodoc import ClassDocumenter
+
+
+class ExperimentDocumenter(ClassDocumenter):
+ """Sphinx extension for the custom documentation of the standard experiment class."""
+
+ objtype = "experiment"
+ directivetype = 'class'
+ priority = 10 + ClassDocumenter.priority
+ option_spec = dict(ClassDocumenter.option_spec)
+
+ @classmethod
+ def can_document_member(
+ cls, member: Any, membername: str, isattr: bool, parent: Any
+ ) -> bool:
+ return isinstance(member, BaseExperiment)
+
+ def add_content(self, more_content: Any, no_docstring: bool = False) -> None:
+ sourcename = self.get_sourcename()
+
+ try:
+ class_doc, init_doc = self.get_doc()
+ except ValueError:
+ raise QiskitError(
+ f"Documentation of {self.name} doesn't match with the expected format."
+ "Please run sphinx build without using the experiment template."
+ )
+
+ # format experiment documentation into the experiment style
+ class_doc_parser = ExperimentDocstring(
+ target_cls=self.object,
+ docstring_lines=class_doc,
+ config=self.env.app.config,
+ indent=self.content_indent,
+ )
+
+ # write introduction
+ for i, line in enumerate(self.process_doc(class_doc_parser.generate_class_docs())):
+ self.add_line(line, sourcename, i)
+ self.add_line("", sourcename)
+
+ # write init method documentation
+ self.add_line(".. rubric:: Initialization", sourcename)
+ self.add_line("", sourcename)
+ for i, line in enumerate(self.process_doc([init_doc])):
+ self.add_line(line, sourcename, i)
+ self.add_line("", sourcename)
+
+ # method and attributes
+ if more_content:
+ for line, src in zip(more_content.data, more_content.items):
+ self.add_line(line, src[0], src[1])
+
+
+def setup(app: Sphinx):
+ existing_documenter = app.registry.documenters.get(ExperimentDocumenter.objtype)
+ if existing_documenter is None or not issubclass(existing_documenter, ExperimentDocumenter):
+ app.add_autodocumenter(ExperimentDocumenter, override=True)
diff --git a/docs/_ext/autoref.py b/docs/_ext/autoref.py
new file mode 100644
index 0000000000..6a4c42a4ae
--- /dev/null
+++ b/docs/_ext/autoref.py
@@ -0,0 +1,105 @@
+# 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.
+
+"""
+Helper directive to generate reference in convenient form.
+"""
+import arxiv
+
+from docutils import nodes
+from docutils.parsers.rst import Directive
+from sphinx.application import Sphinx
+
+
+class WebSite(Directive):
+ """A custom helper directive for showing website link.
+
+ This can be used, for example,
+
+ .. code-block::
+
+ .. ref_website:: qiskit-experiments, https://github.com/Qiskit/qiskit-experiments
+
+ """
+ required_arguments = 1
+ optional_arguments = 0
+ final_argument_whitespace = True
+
+ def run(self):
+ try:
+ name, url = self.arguments[0].split(",")
+ except ValueError:
+ raise ValueError(
+ f"{self.arguments[0]} is invalid website directive format. "
+ "Name and URL should be separated by a single comma."
+ )
+
+ link_name = nodes.paragraph(text=f"{name} ")
+ link_name += nodes.reference(text="(open)", refuri=url)
+
+ return [link_name]
+
+
+class Arxiv(Directive):
+ """A custom helper directive for generating journal information from arXiv id.
+
+ This directive takes two arguments
+
+ - Arbitrary reference name (no white space should be included)
+
+ - arXiv ID
+
+ This can be used, for example,
+
+ .. code-block::
+
+ .. ref_arxiv:: qasm3-paper 2104.14722
+
+ If an article is not found, no journal information will be shown.
+
+ """
+ required_arguments = 2
+ optional_arguments = 0
+ final_argument_whitespace = False
+
+ def run(self):
+
+ # search arXiv database
+ try:
+ search = arxiv.Search(id_list=[self.arguments[1]])
+ paper = next(search.results())
+ except Exception:
+ return []
+
+ # generate journal link nodes
+ ret_node = nodes.paragraph()
+
+ journal = ""
+ if paper.journal_ref:
+ journal += f", {paper.journal_ref}, "
+ if paper.doi:
+ journal += f"doi: {paper.doi}"
+
+ ret_node += nodes.Text(f"[{self.arguments[0]}] ")
+ ret_node += nodes.Text(", ".join([author.name for author in paper.authors]) + ", ")
+ ret_node += nodes.emphasis(text=f"{paper.title}")
+ if journal:
+ ret_node += nodes.Text(journal)
+ ret_node += nodes.Text(" ")
+ ret_node += nodes.reference(text="(open)", refuri=paper.pdf_url)
+
+ return [ret_node]
+
+
+def setup(app: Sphinx):
+ app.add_directive("ref_arxiv", Arxiv)
+ app.add_directive("ref_website", WebSite)
diff --git a/docs/_ext/custom_styles/example/__init__.py b/docs/_ext/custom_styles/example/__init__.py
new file mode 100644
index 0000000000..fc54e4b2c8
--- /dev/null
+++ b/docs/_ext/custom_styles/example/__init__.py
@@ -0,0 +1,48 @@
+# 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.
+
+"""
+=======================================================================
+Example Documentation (:mod:`qiskit_experiments.documentation.example`)
+=======================================================================
+
+.. currentmodule:: qiskit_experiments.documentation.example
+
+
+.. warning::
+
+ This module is just an example for documentation. Do not import.
+
+.. note::
+
+ Under the autosummary directive you need to set template to trigger custom documentation.
+
+
+Experiments
+===========
+.. autosummary::
+ :toctree: ../stubs/
+ :template: autosummary/experiment.rst
+
+ DocumentedExperiment
+
+Analysis
+========
+.. autosummary::
+ :toctree: ../stubs/
+ :template: autosummary/analysis.rst
+
+ DocumentedCurveAnalysis
+
+"""
+
+from .example_experiment import DocumentedExperiment, DocumentedCurveAnalysis
diff --git a/docs/_ext/custom_styles/example/example_experiment.py b/docs/_ext/custom_styles/example/example_experiment.py
new file mode 100644
index 0000000000..1fa3a0ac4a
--- /dev/null
+++ b/docs/_ext/custom_styles/example/example_experiment.py
@@ -0,0 +1,218 @@
+# 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.
+"""
+Class documentation examples.
+
+.. warning::
+
+ This module is just an example for documentation. Do not import.
+
+"""
+
+from qiskit.providers import Options
+
+from qiskit_experiments.curve_analysis.curve_analysis import CurveAnalysis
+from qiskit_experiments.framework.base_experiment import BaseExperiment
+
+
+class DocumentedCurveAnalysis(CurveAnalysis):
+ r"""One line summary of this class. This is shown in the top level contains list.
+
+ # section: overview
+ Overview of this analysis. It is recommended to write this section.
+ Here you can explain technical aspect of fit algorithm or fit model.
+ Standard reStructuredText directives can be used.
+
+ You can use following sections
+
+ - ``warning``
+ - ``note``
+ - ``example``
+ - ``reference``
+ - ``tutorial``
+
+ See :class:`DocumentedExperiment` for description of these sections.
+ In addition to above sections, analysis template provides following extra sections.
+
+ # section: fit_model
+ Here you can describe your fitting model.
+ Standard reStructuredText directives can be used. For example:
+
+ .. math::
+
+ F(x) = a \exp(-(x-f)^2/(2\sigma^2)) + b
+
+ enables you to use the Latex syntax to write your equation.
+
+ # section: fit_parameters
+ Here you can explain fit parameter details.
+ This section provides a special syntax to describe details of each parameter.
+ Documentation except for this syntax will be just ignored.
+
+ defpar a:
+ desc: Description of parameter :math:`a`.
+ init_guess: Here you can describe how this analysis estimate initial guess of
+ parameter :math:`a`.
+ bounds: Here you can describe how this analysis bounds parameter :math:`a` value
+ during the fit.
+
+ defpar b:
+ desc: Description of parameter :math:`b`.
+ init_guess: Here you can describe how this analysis estimate initial guess of
+ parameter :math:`b`.
+ bounds: Here you can describe how this analysis bounds parameter :math:`b` value
+ during the fit.
+
+ Note that you cannot write text block (i.e. bullet lines, math mode, parsed literal, ...)
+ in the ``defpar`` syntax items. These are a single line description of parameters.
+ You can write multiple ``defpar`` block for each fitting parameter.
+
+ It would be nice if parameter names conform to the parameter key values appearing in the
+ analysis result. For example, if fit model defines the parameter :math:`\sigma` and
+ this appears as ``eta`` in the result, user cannot find correspondence of these parameters.
+
+ """
+
+ @classmethod
+ def _default_options(cls) -> Options:
+ """Default analysis options.
+
+ .. note::
+
+ This method documentation should conforms to the below documentation syntax.
+ Namely, the title should be "Analysis Options" followed by a single colon
+ and description should be written in the Google docstring style.
+ Numpy style is not accepted.
+
+ Google style docstring guideline:
+ https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
+
+ Documentation except for the analysis options will be just ignored, e.g. this note.
+ If analysis options contains some values from the parent class,
+ the custom Sphinx parser searches for the parent class method documentation
+ and automatically generate documentation for all available options.
+ If there is any missing documentation the Sphinx build will fail.
+
+ Analysis Options:
+ opt1 (int): Description for the option1.
+ opt2 (bool): Description for the option2.
+ opt3 (str): Description for the option3.
+
+ """
+ opts = super()._default_options()
+ opts.opt1 = 1.0
+ opts.opt2 = True
+ opts.opt3 = "opt3"
+
+ return opts
+
+
+class DocumentedExperiment(BaseExperiment):
+ """One line summary of this class. This is shown in the top level contains list.
+
+ # section: overview
+ Overview of this experiment. It is recommended to write this section.
+ Here you can explain technical aspect of experiment, protocol, etc...
+ Standard reStructuredText directives can be used.
+
+ # section: warning
+ Warning about this experiment if exist.
+ Some functionality is not available or under development,
+ you should write these details here.
+
+ # section: note
+ Notification about this experiment if exist.
+
+ # section: example
+ Example code of this experiment.
+ If this experiment requires user to manage complicated options,
+ it might be convenient for users to have some code example here.
+
+ You can write code example, for example, as follows
+
+ .. code-block:: python
+
+ import qiskit_experiments
+ my_experiment = qiskit_experiments.MyExperiment(**options)
+
+ # section: reference
+ Currently this supports article reference in arXiv database.
+ You can use following helper directive.
+
+ .. ref_arxiv:: Auth2020a 21xx.01xxx
+
+ This directive takes two arguments separated by a whitespace.
+ The first argument is arbitrary label for this article, which may be used to
+ refer to this paper from other sections.
+ Second argument is the arXiv ID of the paper referring to.
+ Once this directive is inserted, Sphinx searches the arXiv database and
+ automatically generates a formatted bibliography with the hyperlink to the online PDF.
+
+ # section: tutorial
+ You can refer to the arbitrary web page here.
+ Following helper directive can be used.
+
+ .. ref_website:: Qiskit Experiment Github, https://github.com/Qiskit/qiskit-experiments
+
+ This directive takes two arguments separated by a comma.
+ The first argument is arbitrary label shown before the link. Whitespace can be included.
+ The second argument is the URL of the website to hyperlink.
+
+ """
+
+ @classmethod
+ def _default_experiment_options(cls) -> Options:
+ """Default experiment options.
+
+ .. note::
+
+ This method documentation should conforms to the below documentation syntax.
+ Namely, the title should be "Experiment Options" followed by a single colon
+ and description should be written in the Google docstring style.
+ Numpy style is not accepted.
+
+ Google style docstring guideline:
+ https://sphinxcontrib-napoleon.readthedocs.io/en/latest/example_google.html
+
+ Documentation except for the experiment options will be just ignored, e.g. this note.
+ If experiment options contains some values from the parent class,
+ the custom Sphinx parser searches for the parent class method documentation
+ and automatically generate documentation for all available options.
+ If there is any missing documentation the Sphinx build will fail.
+
+ Experiment Options:
+ opt1 (int): Description for the option1.
+ opt2 (bool): Description for the option2.
+ opt3 (str): Description for the option3.
+
+ """
+ opts = super()._default_experiment_options()
+ opts.opt1 = 1.0
+ opts.opt2 = True
+ opts.opt3 = "opt3"
+
+ return opts
+
+ def __init__(self, qubit: int):
+ """Create new experiment.
+
+ .. note::
+
+ This documentation is shown as-is.
+
+ Args:
+ qubit: The qubit to run experiment.
+ """
+ super().__init__(qubits=[qubit])
+
+ def circuits(self, backend=None):
+ pass
diff --git a/docs/_ext/custom_styles/formatter.py b/docs/_ext/custom_styles/formatter.py
new file mode 100644
index 0000000000..40c61892ac
--- /dev/null
+++ b/docs/_ext/custom_styles/formatter.py
@@ -0,0 +1,205 @@
+# 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.
+
+"""
+A class that formats documentation sections.
+"""
+from typing import List
+from .utils import _check_no_indent
+
+
+class DocstringSectionFormatter:
+ """A class that formats parsed docstring lines.
+
+ This formatter formats sections with Google Style Python Docstrings with
+ several reStructuredText directives.
+ """
+
+ def __init__(self, indent: str):
+ self.indent = indent
+
+ def format_header(self, lines: List[str]) -> List[str]:
+ """Format header section."""
+ format_lines = lines
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_overview(self, lines: List[str]) -> List[str]:
+ """Format overview section."""
+ format_lines = [".. rubric:: Overview", ""]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_reference(self, lines: List[str]) -> List[str]:
+ """Format reference section."""
+ format_lines = [".. rubric:: References", ""]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ def format_warning(self, lines: List[str]) -> List[str]:
+ """Format warning section."""
+ format_lines = [".. warning::", ""]
+ for line in lines:
+ format_lines.append(self.indent + line)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_example(self, lines: List[str]) -> List[str]:
+ """Format example section."""
+ format_lines = [".. rubric:: Example", ""]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ def format_note(self, lines: List[str]) -> List[str]:
+ """Format notification section."""
+ format_lines = [".. note::", ""]
+ for line in lines:
+ format_lines.append(self.indent + line)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_tutorial(self, lines: List[str]) -> List[str]:
+ """Format tutorial section."""
+ format_lines = [".. rubric:: Tutorials", ""]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+
+class ExperimentSectionFormatter(DocstringSectionFormatter):
+ """Formatter for experiment class."""
+
+ @_check_no_indent
+ def format_analysis_ref(self, lines: List[str]) -> List[str]:
+ """Format analysis class reference section."""
+ format_lines = [".. rubric:: Analysis Class Reference", ""]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_experiment_opts(self, lines: List[str]) -> List[str]:
+ """Format experiment options section."""
+ format_lines = [
+ ".. rubric:: Experiment Options",
+ "",
+ "These options can be set by :py:meth:`set_experiment_options` method.",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_analysis_opts(self, lines: List[str]) -> List[str]:
+ """Format analysis options section."""
+ format_lines = [
+ ".. rubric:: Analysis Options",
+ "",
+ "These options can be set by :py:meth:`set_analysis_options` method.",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_transpiler_opts(self, lines: List[str]) -> List[str]:
+ """Format transpiler options section."""
+ format_lines = [
+ ".. rubric:: Transpiler Options",
+ "",
+ "This option can be set by :py:meth:`set_transpile_options` method.",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_run_opts(self, lines: List[str]) -> List[str]:
+ """Format run options section."""
+ format_lines = [
+ ".. rubric:: Backend Run Options",
+ "",
+ "This option can be set by :py:meth:`set_run_options` method.",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+
+class AnalysisSectionFormatter(DocstringSectionFormatter):
+ """Formatter for analysis class."""
+
+ @_check_no_indent
+ def format_analysis_opts(self, lines: List[str]) -> List[str]:
+ """Format analysis options section."""
+ format_lines = [
+ ".. rubric:: Run Options",
+ "",
+ "These are the keyword arguments of :py:meth:`run` method.",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_fit_model(self, lines: List[str]) -> List[str]:
+ """Format fit model section."""
+ format_lines = [
+ ".. rubric:: Fit Model",
+ "",
+ "This is the curve fitting analysis. ",
+ "Following equation(s) are used to represent curve(s).",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
+
+ @_check_no_indent
+ def format_fit_parameters(self, lines: List[str]) -> List[str]:
+ """Format fit parameter section."""
+ format_lines = [
+ ".. rubric:: Fit Parameters",
+ "",
+ "Following fit parameters are estimated during the analysis.",
+ "",
+ ]
+ format_lines.extend(lines)
+ format_lines.append("")
+
+ return format_lines
diff --git a/docs/_ext/custom_styles/section_parsers.py b/docs/_ext/custom_styles/section_parsers.py
new file mode 100644
index 0000000000..870d2011ae
--- /dev/null
+++ b/docs/_ext/custom_styles/section_parsers.py
@@ -0,0 +1,90 @@
+# 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.
+
+"""
+Documentation section parsers.
+"""
+
+import re
+from typing import List
+
+from .utils import _trim_empty_lines
+
+
+def load_standard_section(docstring_lines: List[str]) -> List[str]:
+ """Load standard docstring section."""
+ return _trim_empty_lines(docstring_lines)
+
+
+def load_fit_parameters(docstring_lines: List[str]) -> List[str]:
+ """Load fit parameter section."""
+ regex_paramdef = re.compile(r"defpar (?P.+):")
+
+ # item finder
+ description_kind = {
+ "desc": re.compile(r"desc: (?P.+)"),
+ "init_guess": re.compile(r"init_guess: (?P.+)"),
+ "bounds": re.compile(r"bounds: (?P.+)"),
+ }
+
+ # parse lines
+ parameter_desc = dict()
+ current_param = None
+ current_item = None
+ for line in docstring_lines:
+ if not list:
+ # remove line feed
+ continue
+
+ # check if line is new parameter definition
+ match = re.match(regex_paramdef, line)
+ if match:
+ current_param = match["param"]
+ parameter_desc[current_param] = {
+ "desc": "",
+ "init_guess": "",
+ "bounds": "",
+ }
+ continue
+
+ # check description
+ for kind, regex in description_kind.items():
+ match = re.search(regex, line)
+ if match:
+ current_item = kind
+ line = match["s"].rstrip()
+
+ # add line if parameter and item are already set
+ if current_param and current_item:
+ if parameter_desc[current_param][current_item]:
+ parameter_desc[current_param][current_item] += " " + line.lstrip()
+ else:
+ parameter_desc[current_param][current_item] = line.lstrip()
+
+ section_lines = list()
+
+ def write_description(header: str, kind: str):
+ section_lines.append(header)
+ for param, desc in parameter_desc.items():
+ if not desc:
+ section_lines.append(
+ f" - :math:`{param}`: No description is provided. See source for details."
+ )
+ else:
+ section_lines.append(f" - :math:`{param}`: {desc[kind]}")
+ section_lines.append("")
+
+ write_description("Descriptions", "desc")
+ write_description("Initial Guess", "init_guess")
+ write_description("Boundaries", "bounds")
+
+ return _trim_empty_lines(section_lines)
diff --git a/docs/_ext/custom_styles/styles.py b/docs/_ext/custom_styles/styles.py
new file mode 100644
index 0000000000..91d2dfaa3c
--- /dev/null
+++ b/docs/_ext/custom_styles/styles.py
@@ -0,0 +1,323 @@
+# 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.
+
+"""
+Documentation extension for experiment class.
+"""
+import copy
+import re
+import sys
+from abc import ABC
+from typing import Union, List, Dict
+
+from qiskit_experiments.framework.base_analysis import BaseAnalysis
+from qiskit_experiments.framework.base_experiment import BaseExperiment
+from sphinx.config import Config as SphinxConfig
+
+from .formatter import (
+ ExperimentSectionFormatter,
+ AnalysisSectionFormatter,
+ DocstringSectionFormatter,
+)
+from .section_parsers import load_standard_section, load_fit_parameters
+from .utils import _generate_options_documentation, _format_default_options
+
+section_regex = re.compile(r"# section: (?P\S+)")
+
+
+class QiskitExperimentDocstring(ABC):
+ """Qiskit Experiment style docstring parser base class."""
+
+ # mapping of sections supported by this style to parsing method or function
+ __sections__ = {}
+
+ # section formatter
+ __formatter__ = DocstringSectionFormatter
+
+ def __init__(
+ self,
+ target_cls: object,
+ docstring_lines: Union[str, List[str]],
+ config: SphinxConfig,
+ indent: str = "",
+ ):
+ """Create new parser and parse formatted docstring."""
+
+ if isinstance(docstring_lines, str):
+ lines = docstring_lines.splitlines()
+ else:
+ lines = docstring_lines
+
+ self._target_cls = target_cls
+ self._indent = indent
+ self._config = config
+
+ self._parsed_lines = self._classify(lines)
+
+ def _classify(self, docstrings: List[str]) -> Dict[str, List[str]]:
+ """Classify formatted docstring into sections."""
+ sectioned_docstrings = dict()
+
+ def add_new_section(section: str, lines: List[str]):
+ if lines:
+ parser = self.__sections__[section]
+ if not parser:
+ raise KeyError(
+ f"Section {section} is automatically generated section. "
+ "This section cannot be overridden by class docstring."
+ )
+ sectioned_docstrings[section] = parser(temp_lines)
+
+ current_section = list(self.__sections__.keys())[0]
+ temp_lines = list()
+ margin = sys.maxsize
+ for docstring_line in docstrings:
+ match = re.match(section_regex, docstring_line.strip())
+ if match:
+ section_name = match["section_name"]
+ if section_name in self.__sections__:
+ # parse previous section
+ if margin < sys.maxsize:
+ temp_lines = [l[margin:] for l in temp_lines]
+ add_new_section(current_section, temp_lines)
+ # set new section
+ current_section = section_name
+ temp_lines.clear()
+ margin = sys.maxsize
+ else:
+ raise KeyError(f"Section name {section_name} is invalid.")
+ continue
+
+ # calculate section indent
+ if len(docstring_line) > 0 and not docstring_line.isspace():
+ # ignore empty line
+ indent = len(docstring_line) - len(docstring_line.lstrip())
+ margin = min(indent, margin)
+
+ temp_lines.append(docstring_line)
+
+ # parse final section
+ if margin < sys.maxsize:
+ temp_lines = [l[margin:] for l in temp_lines]
+ add_new_section(current_section, temp_lines)
+
+ for section, lines in self._extra_sections().items():
+ sectioned_docstrings[section] = lines
+
+ return sectioned_docstrings
+
+ def _extra_sections(self) -> Dict[str, List[str]]:
+ """Generate extra sections."""
+ pass
+
+ def _format(self) -> Dict[str, List[str]]:
+ """Format each section with predefined formatter."""
+ formatter = self.__formatter__(self._indent)
+
+ formatted_sections = {section: None for section in self.__sections__}
+ for section, lines in self._parsed_lines.items():
+ if not lines:
+ continue
+ section_formatter = getattr(formatter, f"format_{section}", None)
+ if section_formatter:
+ formatted_sections[section] = section_formatter(lines)
+ else:
+ formatted_sections[section] = lines + [""]
+
+ return formatted_sections
+
+ def generate_class_docs(self) -> List[List[str]]:
+ """Output formatted experiment class documentation."""
+ formatted_sections = self._format()
+
+ classdoc_lines = []
+ for section_lines in formatted_sections.values():
+ if section_lines:
+ classdoc_lines.extend(section_lines)
+
+ return [classdoc_lines]
+
+
+class ExperimentDocstring(QiskitExperimentDocstring):
+ """Documentation parser for the experiment class introduction."""
+
+ __sections__ = {
+ "header": load_standard_section,
+ "warning": load_standard_section,
+ "overview": load_standard_section,
+ "reference": load_standard_section,
+ "tutorial": load_standard_section,
+ "analysis_ref": None,
+ "experiment_opts": None,
+ "analysis_opts": None,
+ "transpiler_opts": None,
+ "run_opts": None,
+ "example": load_standard_section,
+ "note": load_standard_section,
+ }
+
+ __formatter__ = ExperimentSectionFormatter
+
+ def __init__(
+ self,
+ target_cls: BaseExperiment,
+ docstring_lines: Union[str, List[str]],
+ config: SphinxConfig,
+ indent: str = "",
+ ):
+ """Create new parser and parse formatted docstring."""
+ super().__init__(target_cls, docstring_lines, config, indent)
+
+ def _extra_sections(self) -> Dict[str, List[str]]:
+ """Generate extra sections."""
+ parsed_sections = {}
+
+ # add analysis class reference
+ analysis_class = getattr(self._target_cls, "__analysis_class__", None)
+ if analysis_class:
+ analysis_ref = f":py:class:`~{analysis_class.__module__}.{analysis_class.__name__}`"
+ parsed_sections["analysis_ref"] = [analysis_ref]
+
+ # add experiment option
+ exp_option_desc = []
+
+ exp_docs_config = copy.copy(self._config)
+ exp_docs_config.napoleon_custom_sections = [("experiment options", "args")]
+ exp_option = _generate_options_documentation(
+ current_class=self._target_cls,
+ method_name="_default_experiment_options",
+ config=exp_docs_config,
+ indent=self._indent,
+ )
+ if exp_option:
+ exp_option_desc.extend(exp_option)
+ exp_option_desc.append("")
+ exp_option_desc.extend(
+ _format_default_options(
+ defaults=self._target_cls._default_experiment_options().__dict__,
+ indent=self._indent,
+ )
+ )
+ else:
+ exp_option_desc.append("No experiment option available for this experiment.")
+
+ parsed_sections["experiment_opts"] = exp_option_desc
+
+ # add analysis option
+ analysis_option_desc = []
+
+ if analysis_class:
+ analysis_docs_config = copy.copy(self._config)
+ analysis_docs_config.napoleon_custom_sections = [("analysis options", "args")]
+ analysis_option = _generate_options_documentation(
+ current_class=analysis_class,
+ method_name="_default_options",
+ config=analysis_docs_config,
+ indent=self._indent,
+ )
+
+ if analysis_option:
+ analysis_option_desc.extend(analysis_option)
+ analysis_option_desc.append("")
+ analysis_option_desc.extend(
+ _format_default_options(
+ defaults=analysis_class._default_options().__dict__,
+ indent=self._indent,
+ )
+ )
+ else:
+ analysis_option_desc.append("No analysis option available for this experiment.")
+
+ parsed_sections["analysis_opts"] = analysis_option_desc
+
+ # add transpiler option
+ transpiler_option_desc = [
+ "This option is used for circuit optimization. ",
+ "See `Qiskit Transpiler `_ documentation for available options.",
+ "",
+ ]
+ transpiler_option_desc.extend(
+ _format_default_options(
+ defaults=self._target_cls._default_transpile_options().__dict__,
+ indent=self._indent,
+ )
+ )
+
+ parsed_sections["transpiler_opts"] = transpiler_option_desc
+
+ # add run option
+ run_option_desc = [
+ "This option is used for controlling job execution condition. "
+ "Note that this option is provider dependent. "
+ "See provider's backend runner API for available options. "
+ "See `here `_ for IBM Quantum Service.",
+ "",
+ ]
+ run_option_desc.extend(
+ _format_default_options(
+ defaults=self._target_cls._default_run_options().__dict__,
+ indent=self._indent,
+ )
+ )
+
+ parsed_sections["run_opts"] = run_option_desc
+
+ return parsed_sections
+
+
+class AnalysisDocstring(QiskitExperimentDocstring):
+ """Documentation parser for the analysis class introduction."""
+
+ __sections__ = {
+ "header": load_standard_section,
+ "warning": load_standard_section,
+ "overview": load_standard_section,
+ "fit_model": load_standard_section,
+ "fit_parameters": load_fit_parameters,
+ "reference": load_standard_section,
+ "tutorial": load_standard_section,
+ "analysis_opts": None,
+ "example": load_standard_section,
+ "note": load_standard_section,
+ }
+
+ __formatter__ = AnalysisSectionFormatter
+
+ def __init__(
+ self,
+ target_cls: BaseAnalysis,
+ docstring_lines: Union[str, List[str]],
+ config: SphinxConfig,
+ indent: str = "",
+ ):
+ """Create new parser and parse formatted docstring."""
+ super().__init__(target_cls, docstring_lines, config, indent)
+
+ def _extra_sections(self) -> Dict[str, List[str]]:
+ """Generate extra sections."""
+ parsed_sections = {}
+
+ # add analysis option
+ analysis_docs_config = copy.copy(self._config)
+ analysis_docs_config.napoleon_custom_sections = [("analysis options", "args")]
+ analysis_option = _generate_options_documentation(
+ current_class=self._target_cls,
+ method_name="_default_options",
+ config=analysis_docs_config,
+ indent=self._indent,
+ )
+ if analysis_option:
+ parsed_sections["analysis_opts"] = analysis_option
+
+ return parsed_sections
diff --git a/docs/_ext/custom_styles/utils.py b/docs/_ext/custom_styles/utils.py
new file mode 100644
index 0000000000..46e7e63381
--- /dev/null
+++ b/docs/_ext/custom_styles/utils.py
@@ -0,0 +1,162 @@
+# 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.
+"""
+A collection of utilities to generate documentation.
+"""
+
+import inspect
+import re
+from typing import List, Tuple, Dict, Any, Callable
+
+from sphinx.config import Config as SphinxConfig
+from sphinx.ext.napoleon.docstring import GoogleDocstring
+from sphinx.util.docstrings import prepare_docstring
+
+
+def _trim_empty_lines(docstring_lines: List[str]) -> List[str]:
+ """A helper function to remove redundant line feeds."""
+ i_start = 0
+ lines_iter = iter(docstring_lines)
+ while not next(lines_iter):
+ i_start += 1
+
+ i_end = len(docstring_lines)
+ lines_iter = iter(docstring_lines[::-1])
+ while not next(lines_iter):
+ i_end -= 1
+
+ return docstring_lines[i_start:i_end]
+
+
+def _parse_option_field(
+ docstring: str,
+ config: SphinxConfig,
+ target_args: List[str],
+ indent: str = "",
+) -> Tuple[List[str], List[str]]:
+ """A helper function to extract descriptions of target arguments."""
+
+ # use GoogleDocstring parameter parser
+ experiment_option_parser = GoogleDocstring(
+ docstring=prepare_docstring(docstring, tabsize=len(indent)), config=config
+ )
+ parsed_lines = experiment_option_parser.lines()
+
+ # remove redundant descriptions
+ param_regex = re.compile(r":(param|type) (?P\S+):")
+ target_params_description = []
+ described_params = set()
+ valid_line = False
+ for line in parsed_lines:
+ is_item = re.match(param_regex, line)
+ if is_item:
+ if is_item["pname"] in target_args:
+ valid_line = True
+ described_params.add(is_item["pname"])
+ else:
+ valid_line = False
+ if valid_line:
+ target_params_description.append(line)
+
+ # find missing parameters
+ missing = set(target_args) - described_params
+
+ return target_params_description, list(missing)
+
+
+def _generate_options_documentation(
+ current_class: object,
+ method_name: str,
+ target_args: List[str] = None,
+ config: SphinxConfig = None,
+ indent: str = "",
+) -> List[str]:
+ """Automatically generate documentation from the default options method."""
+
+ if current_class == object:
+ # check if no more base class
+ raise Exception(f"Option docstring for {', '.join(target_args)} is missing.")
+
+ options_docstring_lines = []
+
+ default_opts = getattr(current_class, method_name, None)
+ if not default_opts:
+ # getter option is not defined
+ return []
+
+ if not target_args:
+ target_args = list(default_opts().__dict__.keys())
+
+ # parse default options method
+ parsed_lines, target_args = _parse_option_field(
+ docstring=default_opts.__doc__ or "",
+ config=config,
+ target_args=target_args,
+ indent=indent,
+ )
+
+ if target_args:
+ # parse parent class method docstring if some arg documentation is missing
+ parent_parsed_lines = _generate_options_documentation(
+ current_class=inspect.getmro(current_class)[1],
+ method_name=method_name,
+ target_args=target_args,
+ config=config,
+ indent=indent,
+ )
+ options_docstring_lines.extend(parent_parsed_lines)
+
+ options_docstring_lines.extend(parsed_lines)
+
+ if options_docstring_lines:
+ return _trim_empty_lines(options_docstring_lines)
+
+ return options_docstring_lines
+
+
+def _format_default_options(defaults: Dict[str, Any], indent: str = "") -> List[str]:
+ """Format default options to docstring lines."""
+ docstring_lines = [
+ ".. dropdown:: Default values",
+ indent + ":animate: fade-in-slide-down",
+ "",
+ ]
+
+ if not defaults:
+ docstring_lines.append(indent + "No default options are set.")
+ else:
+ docstring_lines.append(indent + "Following values are set by default.")
+ docstring_lines.append("")
+ docstring_lines.append(indent + ".. parsed-literal::")
+ docstring_lines.append("")
+ for par, value in defaults.items():
+ if callable(value):
+ value_repr = f"Callable {value.__name__}"
+ else:
+ value_repr = repr(value)
+ docstring_lines.append(indent * 2 + f"{par:<25} := {value_repr}")
+
+ return docstring_lines
+
+
+def _check_no_indent(method: Callable) -> Callable:
+ """Check indent of lines and return if this block is correctly indented."""
+ def wraps(self, lines: List[str], *args, **kwargs):
+ if all(l.startswith(" ") for l in lines):
+ text_block = "\n".join(lines)
+ raise ValueError(
+ "Following documentation may have invalid indentation. "
+ f"Please carefully check all indent levels are aligned. \n\n{text_block}"
+ )
+ return method(self, lines, *args, **kwargs)
+
+ return wraps
diff --git a/docs/_templates/autosummary/analysis.rst b/docs/_templates/autosummary/analysis.rst
new file mode 100644
index 0000000000..222df215af
--- /dev/null
+++ b/docs/_templates/autosummary/analysis.rst
@@ -0,0 +1,49 @@
+{% if referencefile %}
+.. include:: {{ referencefile }}
+{% endif %}
+
+{{ objname }}
+{{ underline }}
+
+.. currentmodule:: {{ module }}
+
+.. autoanalysis:: {{ objname }}
+ :no-members:
+ :no-inherited-members:
+ :no-special-members:
+
+ {% block attributes_summary %}
+ {% if attributes %}
+
+ .. rubric:: Attributes
+
+ .. autosummary::
+ :toctree: ../stubs/
+ {% for item in all_attributes %}
+ {%- if not item.startswith('_') %}
+ {{ name }}.{{ item }}
+ {%- endif -%}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+ {% block methods_summary %}
+ {% if methods %}
+
+ .. rubric:: Methods
+
+ .. autosummary::
+ :toctree: ../stubs/
+ {% for item in all_methods %}
+ {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %}
+ {{ name }}.{{ item }}
+ {%- endif -%}
+ {%- endfor %}
+ {% for item in inherited_members %}
+ {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %}
+ {{ name }}.{{ item }}
+ {%- endif -%}
+ {%- endfor %}
+
+ {% endif %}
+ {% endblock %}
diff --git a/docs/_templates/autosummary/experiment.rst b/docs/_templates/autosummary/experiment.rst
new file mode 100644
index 0000000000..01800ea10b
--- /dev/null
+++ b/docs/_templates/autosummary/experiment.rst
@@ -0,0 +1,49 @@
+{% if referencefile %}
+.. include:: {{ referencefile }}
+{% endif %}
+
+{{ objname }}
+{{ underline }}
+
+.. currentmodule:: {{ module }}
+
+.. autoexperiment:: {{ objname }}
+ :no-members:
+ :no-inherited-members:
+ :no-special-members:
+
+ {% block attributes_summary %}
+ {% if attributes %}
+
+ .. rubric:: Attributes
+
+ .. autosummary::
+ :toctree: ../stubs/
+ {% for item in all_attributes %}
+ {%- if not item.startswith('_') %}
+ {{ name }}.{{ item }}
+ {%- endif -%}
+ {%- endfor %}
+ {% endif %}
+ {% endblock %}
+
+ {% block methods_summary %}
+ {% if methods %}
+
+ .. rubric:: Methods
+
+ .. autosummary::
+ :toctree: ../stubs/
+ {% for item in all_methods %}
+ {%- if not item.startswith('_') or item in ['__call__', '__mul__', '__getitem__', '__len__'] %}
+ {{ name }}.{{ item }}
+ {%- endif -%}
+ {%- endfor %}
+ {% for item in inherited_members %}
+ {%- if item in ['__call__', '__mul__', '__getitem__', '__len__'] %}
+ {{ name }}.{{ item }}
+ {%- endif -%}
+ {%- endfor %}
+
+ {% endif %}
+ {% endblock %}
diff --git a/docs/conf.py b/docs/conf.py
index 79c501e4fd..9fed00b22b 100644
--- a/docs/conf.py
+++ b/docs/conf.py
@@ -23,9 +23,10 @@
# add these directories to sys.path here. If the directory is relative to the
# documentation root, use os.path.abspath to make it absolute, like shown here.
#
-# import os
-# import sys
-# sys.path.insert(0, os.path.abspath('.'))
+import os
+import sys
+sys.path.insert(0, os.path.abspath('.'))
+sys.path.append(os.path.abspath("./_ext"))
"""
Sphinx documentation builder
@@ -37,7 +38,7 @@
os.environ['QISKIT_DOCS'] = 'TRUE'
# -- Project information -----------------------------------------------------
-project = 'Qiskit ODE Solvers'
+project = 'Qiskit Experiments'
copyright = '2021, Qiskit Development Team' # pylint: disable=redefined-builtin
author = 'Qiskit Development Team'
@@ -92,6 +93,9 @@
'sphinx_panels',
'sphinx.ext.intersphinx',
'nbsphinx',
+ 'autoref',
+ 'autodoc_experiment',
+ 'autodoc_analysis',
]
html_static_path = ['_static']
templates_path = ['_templates']
diff --git a/qiskit_experiments/curve_analysis/curve_analysis.py b/qiskit_experiments/curve_analysis/curve_analysis.py
index a31a4d5a43..d43029201d 100644
--- a/qiskit_experiments/curve_analysis/curve_analysis.py
+++ b/qiskit_experiments/curve_analysis/curve_analysis.py
@@ -17,6 +17,7 @@
import dataclasses
import inspect
+from abc import ABC
import functools
from typing import Any, Dict, List, Tuple, Callable, Union, Optional
@@ -41,7 +42,7 @@
from qiskit_experiments.curve_analysis.utils import get_opt_value, get_opt_error
-class CurveAnalysis(BaseAnalysis):
+class CurveAnalysis(BaseAnalysis, ABC):
"""A base class for curve fit type analysis.
The subclasses can override class attributes to define the behavior of
@@ -270,48 +271,29 @@ def __init__(self):
setattr(self, f"__{key}", None)
@classmethod
- def _default_options(cls):
+ def _default_options(cls) -> Options:
"""Return default analysis options.
- Options:
- curve_fitter: A callback function to perform fitting with formatted data.
- This function should have signature:
-
- .. code-block::
-
- def curve_fitter(
- funcs: List[Callable],
- series: ndarray,
- xdata: ndarray,
- ydata: ndarray,
- p0: ndarray,
- sigma: Optional[ndarray],
- weights: Optional[ndarray],
- bounds: Optional[
- Union[Dict[str, Tuple[float, float]], Tuple[ndarray, ndarray]]
- ],
- ) -> CurveAnalysisResultData:
-
- See :func:`~qiskit_experiment.curve_analysis.multi_curve_fit` for example.
- data_processor: A callback function to format experiment data.
- This function should have signature:
-
- .. code-block::
-
- def data_processor(data: Dict[str, Any]) -> Tuple[float, float]
-
- This can be a :class:`~qiskit_experiment.data_processing.DataProcessor`
+ Analysis Options:
+ curve_fitter (Callable): A callback function to perform fitting with formatted data.
+ See :func:`~qiskit_experiments.analysis.multi_curve_fit` for example.
+ data_processor (Callable): A callback function to format experiment data.
+ This can be a :class:`~qiskit_experiments.data_processing.DataProcessor`
instance that defines the `self.__call__` method.
- normalization: Set ``True`` to normalize y values within range [-1, 1].
- p0: Array-like or dictionary of initial parameters.
- bounds: Array-like or dictionary of (min, max) tuple of fit parameter boundaries.
- x_key: Circuit metadata key representing a scanned value.
- plot: Set ``True`` to create figure for fit result.
- axis: Optional. A matplotlib axis object to draw.
- xlabel: X label of fit result figure.
- ylabel: Y label of fit result figure.
- fit_reports: Mapping of fit parameters and representation in the fit report.
- return_data_points: Set ``True`` to return formatted XY data.
+ normalization (bool) : Set ``True`` to normalize y values within range [-1, 1].
+ p0 (Dict[str, float]): Array-like or dictionary
+ of initial parameters.
+ bounds (Dict[str, Tuple[float, float]]): Array-like or dictionary
+ of (min, max) tuple of fit parameter boundaries.
+ x_key (str): Circuit metadata key representing a scanned value.
+ plot (bool): Set ``True`` to create figure for fit result.
+ axis (AxesSubplot): Optional. A matplotlib axis object to draw.
+ xlabel (str): X label of fit result figure.
+ ylabel (str): Y label of fit result figure.
+ ylim (Tuple[float, float]): Min and max height limit of fit plot.
+ fit_reports (Dict[str, str]): Mapping of fit parameters and representation
+ in the fit report.
+ return_data_points (bool): Set ``True`` to return formatted XY data.
"""
return Options(
curve_fitter=multi_curve_fit,
diff --git a/requirements-dev.txt b/requirements-dev.txt
index cd0c8eb3a5..10ab15adf9 100644
--- a/requirements-dev.txt
+++ b/requirements-dev.txt
@@ -10,6 +10,7 @@ pygments>=2.4
reno>=3.2.0
sphinx-panels
nbsphinx
+arxiv
ddt~=1.4.2
qiskit-aer>=0.8.0