From 18bbb43f51437025f91d30e52d00594e6ae92825 Mon Sep 17 00:00:00 2001 From: Manoel Marques Date: Wed, 19 May 2021 10:45:09 -0400 Subject: [PATCH] Add GettingStarted for Qiskit Nature (#191) (#194) * Add GettingStarted for Qiskit Nature * Add docs path to conf.py sys.path * Update docs/getting_started.rst Co-authored-by: Max Rossmannek * Update docs/getting_started.rst Co-authored-by: Max Rossmannek * Update docs/getting_started.rst Co-authored-by: Max Rossmannek * Update docs/getting_started.rst Co-authored-by: Max Rossmannek * Update docs/getting_started.rst Co-authored-by: Max Rossmannek * Update text Co-authored-by: Manoel Marques Co-authored-by: Max Rossmannek Co-authored-by: Panagiotis Barkoutsos Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com> Co-authored-by: Max Rossmannek Co-authored-by: Panagiotis Barkoutsos --- docs/conf.py | 22 ++- docs/custom_directives.py | 345 ++++++++++++++++++++++++++++++++++++++ docs/getting_started.rst | 131 +++++++++++++++ docs/index.rst | 1 + requirements-dev.txt | 3 +- 5 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 docs/custom_directives.py create mode 100644 docs/getting_started.rst diff --git a/docs/conf.py b/docs/conf.py index e84ce029e2..c719a3f1ba 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('.')) """ Sphinx documentation builder @@ -34,6 +35,10 @@ import os import qiskit_sphinx_theme import qiskit_nature +from custom_directives import (IncludeDirective, GalleryItemDirective, + CustomGalleryItemDirective, CustomCalloutItemDirective, + CustomCardItemDirective) + # Set env flag so that we can doc functions that may otherwise not be loaded # see for example interactive visualizations in qiskit.visualization. os.environ['QISKIT_DOCS'] = 'TRUE' @@ -87,7 +92,7 @@ 'sphinx.ext.mathjax', 'sphinx.ext.viewcode', 'sphinx.ext.extlinks', - 'sphinx_tabs.tabs', + 'sphinx_panels', 'jupyter_sphinx', 'sphinx_autodoc_typehints', 'reno.sphinxext', @@ -188,3 +193,12 @@ 'includehidden': True, 'titles_only': False, } + +# -- Extension configuration ------------------------------------------------- + +def setup(app): + app.add_directive('includenodoc', IncludeDirective) + app.add_directive('galleryitem', GalleryItemDirective) + app.add_directive('customgalleryitem', CustomGalleryItemDirective) + app.add_directive('customcarditem', CustomCardItemDirective) + app.add_directive('customcalloutitem', CustomCalloutItemDirective) diff --git a/docs/custom_directives.py b/docs/custom_directives.py new file mode 100644 index 0000000000..cfb1174409 --- /dev/null +++ b/docs/custom_directives.py @@ -0,0 +1,345 @@ +from docutils.parsers.rst import Directive, directives +from docutils.statemachine import StringList +from docutils import nodes +import re +import os +import sphinx_gallery + +try: + FileNotFoundError +except NameError: + FileNotFoundError = IOError + + +class IncludeDirective(Directive): + """Include source file without docstring at the top of file. + + Implementation just replaces the first docstring found in file + with '' once. + + Example usage: + + .. includenodoc:: /beginner/examples_tensor/two_layer_net_tensor.py + + """ + + # defines the parameter the directive expects + # directives.unchanged means you get the raw value from RST + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + has_content = False + add_index = False + + docstring_pattern = r'"""(?P(?:.|[\r\n])*?)"""\n' + docstring_regex = re.compile(docstring_pattern) + + def run(self): + document = self.state.document + env = document.settings.env + rel_filename, filename = env.relfn2path(self.arguments[0]) + + try: + text = open(filename).read() + text_no_docstring = self.docstring_regex.sub('', text, count=1) + + code_block = nodes.literal_block(text=text_no_docstring) + return [code_block] + except FileNotFoundError as e: + print(e) + return [] + + +class GalleryItemDirective(Directive): + """ + Create a sphinx gallery thumbnail for insertion anywhere in docs. + + Optionally, you can specify the custom figure and intro/tooltip for the + thumbnail. + + Example usage: + + .. galleryitem:: intermediate/char_rnn_generation_tutorial.py + :figure: _static/img/char_rnn_generation.png + :intro: Put your custom intro here. + + If figure is specified, a thumbnail will be made out of it and stored in + _static/thumbs. Therefore, consider _static/thumbs as a 'built' directory. + """ + + required_arguments = 1 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'figure': directives.unchanged, + 'intro': directives.unchanged} + has_content = False + add_index = False + + def run(self): + args = self.arguments + fname = args[-1] + + env = self.state.document.settings.env + fname, abs_fname = env.relfn2path(fname) + basename = os.path.basename(fname) + dirname = os.path.dirname(fname) + + try: + if 'intro' in self.options: + intro = self.options['intro'][:195] + '...' + else: + _, blocks = sphinx_gallery.gen_rst.split_code_and_text_blocks(abs_fname) + intro, _ = sphinx_gallery.gen_rst.extract_intro_and_title(abs_fname, blocks[0][1]) + + thumbnail_rst = sphinx_gallery.backreferences._thumbnail_div( + dirname, basename, intro) + + if 'figure' in self.options: + rel_figname, figname = env.relfn2path(self.options['figure']) + save_figname = os.path.join('_static/thumbs/', + os.path.basename(figname)) + + try: + os.makedirs('_static/thumbs') + except OSError: + pass + + sphinx_gallery.gen_rst.scale_image(figname, save_figname, + 400, 280) + # replace figure in rst with simple regex + thumbnail_rst = re.sub(r'..\sfigure::\s.*\.png', + '.. figure:: /{}'.format(save_figname), + thumbnail_rst) + + thumbnail = StringList(thumbnail_rst.split('\n')) + thumb = nodes.paragraph() + self.state.nested_parse(thumbnail, self.content_offset, thumb) + + return [thumb] + except FileNotFoundError as e: + print(e) + return [] + + +GALLERY_TEMPLATE = """ +.. raw:: html + +
+ +.. only:: html + + .. figure:: {thumbnail} + + {description} + +.. raw:: html + +
+""" + + +class CustomGalleryItemDirective(Directive): + """Create a sphinx gallery style thumbnail. + + tooltip and figure are self explanatory. Description could be a link to + a document like in below example. + + Example usage: + + .. customgalleryitem:: + :tooltip: I am writing this tutorial to focus specifically on NLP for people who have never written code in any deep learning framework + :figure: /_static/img/thumbnails/babel.jpg + :description: :doc:`/beginner/deep_learning_nlp_tutorial` + + If figure is specified, a thumbnail will be made out of it and stored in + _static/thumbs. Therefore, consider _static/thumbs as a 'built' directory. + """ + + required_arguments = 0 + optional_arguments = 0 + final_argument_whitespace = True + option_spec = {'tooltip': directives.unchanged, + 'figure': directives.unchanged, + 'description': directives.unchanged} + + has_content = False + add_index = False + + def run(self): + try: + if 'tooltip' in self.options: + tooltip = self.options['tooltip'][:195] + '...' + else: + raise ValueError('tooltip not found') + + if 'figure' in self.options: + env = self.state.document.settings.env + rel_figname, figname = env.relfn2path(self.options['figure']) + thumbnail = os.path.join('_static/thumbs/', os.path.basename(figname)) + + try: + os.makedirs('_static/thumbs') + except FileExistsError: + pass + + sphinx_gallery.gen_rst.scale_image(figname, thumbnail, 400, 280) + else: + thumbnail = '_static/img/thumbnails/default.png' + + if 'description' in self.options: + description = self.options['description'] + else: + raise ValueError('description not doc found') + + except FileNotFoundError as e: + print(e) + return [] + except ValueError as e: + print(e) + raise + return [] + + thumbnail_rst = GALLERY_TEMPLATE.format(tooltip=tooltip, + thumbnail=thumbnail, + description=description) + thumbnail = StringList(thumbnail_rst.split('\n')) + thumb = nodes.paragraph() + self.state.nested_parse(thumbnail, self.content_offset, thumb) + return [thumb] + + +class CustomCardItemDirective(Directive): + option_spec = {'header': directives.unchanged, + 'image': directives.unchanged, + 'link': directives.unchanged, + 'card_description': directives.unchanged, + 'tags': directives.unchanged} + + def run(self): + try: + if 'header' in self.options: + header = self.options['header'] + else: + raise ValueError('header not doc found') + + if 'image' in self.options: + image = "" + else: + image = '_static/img/thumbnails/default.png' + + if 'link' in self.options: + link = self.options['link'] + else: + link = '' + + if 'card_description' in self.options: + card_description = self.options['card_description'] + else: + card_description = '' + + if 'tags' in self.options: + tags = self.options['tags'] + else: + tags = '' + + except FileNotFoundError as e: + print(e) + return [] + except ValueError as e: + print(e) + raise + return [] + + card_rst = CARD_TEMPLATE.format(header=header, + image=image, + link=link, + card_description=card_description, + tags=tags) + card_list = StringList(card_rst.split('\n')) + card = nodes.paragraph() + self.state.nested_parse(card_list, self.content_offset, card) + return [card] + + +CARD_TEMPLATE = """ +.. raw:: html + +
+ +
+ +
+ +
+

{header}

+
+ +

{card_description}

+ +

{tags}

+ +
{image}
+ +
+ +
+ +
+""" + +class CustomCalloutItemDirective(Directive): + option_spec = {'header': directives.unchanged, + 'description': directives.unchanged, + 'button_link': directives.unchanged, + 'button_text': directives.unchanged} + + def run(self): + try: + if 'description' in self.options: + description = self.options['description'] + else: + description = '' + + if 'header' in self.options: + header = self.options['header'] + else: + raise ValueError('header not doc found') + + if 'button_link' in self.options: + button_link = self.options['button_link'] + else: + button_link = '' + + if 'button_text' in self.options: + button_text = self.options['button_text'] + else: + button_text = '' + + except FileNotFoundError as e: + print(e) + return [] + except ValueError as e: + print(e) + raise + return [] + + callout_rst = CALLOUT_TEMPLATE.format(description=description, + header=header, + button_link=button_link, + button_text=button_text) + callout_list = StringList(callout_rst.split('\n')) + callout = nodes.paragraph() + self.state.nested_parse(callout_list, self.content_offset, callout) + return [callout] + +CALLOUT_TEMPLATE = """ +.. raw:: html + +
+
+

{header}

+

{description}

+ {button_text} +
+
+""" diff --git a/docs/getting_started.rst b/docs/getting_started.rst new file mode 100644 index 0000000000..e843817522 --- /dev/null +++ b/docs/getting_started.rst @@ -0,0 +1,131 @@ +:orphan: + +############### +Getting started +############### + +Installation +============ + +Qiskit Nature depends on the main Qiskit package which has its own +`Qiskit Getting Started `__ detailing the +installation options for Qiskit and its supported environments/platforms. You should refer to +that first. Then the information here can be followed which focuses on the additional installation +specific to Qiskit Nature. + +Qiskit Nature has some functions that have been made optional where the dependent code and/or +support program(s) are not (or cannot be) installed by default. These include, for example, +classical library/programs for molecular problems. +See :ref:`optional_installs` for more information. + +.. tabbed:: Start locally + + The simplest way to get started is to follow the getting started 'Start locally' for Qiskit + here `Qiskit Getting Started `__ + + In your virtual environment where you installed Qiskit simply add ``nature`` to the + extra list in a similar manner to how the extra ``visualization`` support is installed for + Qiskit, i.e: + + .. code:: sh + + pip install qiskit[nature] + + It is worth pointing out that if you're a zsh user (which is the default shell on newer + versions of macOS), you'll need to put ``qiskit[nature]`` in quotes: + + .. code:: sh + + pip install 'qiskit[nature]' + + +.. tabbed:: Install from source + + Installing Qiskit Nature from source allows you to access the most recently + updated version under development instead of using the version in the Python Package + Index (PyPI) repository. This will give you the ability to inspect and extend + the latest version of the Qiskit Nature code more efficiently. + + Since Qiskit Nature depends on Qiskit, and its latest changes may require new or changed + features of Qiskit, you should first follow Qiskit's `"Install from source"` instructions + here `Qiskit Getting Started `__ + + .. raw:: html + +

Installing Qiskit Nature from Source

+ + Using the same development environment that you installed Qiskit in you are ready to install + Qiskit Nature. + + 1. Clone the Qiskit Nature repository. + + .. code:: sh + + git clone https://github.com/Qiskit/qiskit-nature.git + + 2. Cloning the repository creates a local folder called ``qiskit-nature``. + + .. code:: sh + + cd qiskit-nature + + 3. If you want to run tests or linting checks, install the developer requirements. + + .. code:: sh + + pip install -r requirements-dev.txt + + 4. Install ``qiskit-nature``. + + .. code:: sh + + pip install . + + If you want to install it in editable mode, meaning that code changes to the + project don't require a reinstall to be applied, you can do this with: + + .. code:: sh + + pip install -e . + + +.. _optional_installs: + +Optional installs +================= + +Qiskit Nature supports the use of different classical libraries and programs, via drivers, which +compute molecular information, such as one and two body integrals. This is needed as problem input to +algorithms that compute properties of molecules, such as the ground state energy, so at least one such +library/program should be installed. As you can choose which driver you use, you can install as +many, or as few as you wish, that are supported by your platform etc. + +See `Driver installation <./apidocs/qiskit_nature.drivers.html#drivers>`__ which lists each driver +and how to install the dependent library/program that it requires. + +---- + +Ready to get going?... +====================== + +.. raw:: html + +
+
+ +.. customcalloutitem:: + :description: Find out about Qiskit Nature and how to use it for natural science problems. + :header: Dive into the tutorials + :button_link: ./tutorials/index.html + :button_text: Qiskit Nature tutorials + +.. raw:: html + +
+
+ + +.. Hiding - Indices and tables + :ref:`genindex` + :ref:`modindex` + :ref:`search` diff --git a/docs/index.rst b/docs/index.rst index 68e08c7c97..8c05c3581f 100644 --- a/docs/index.rst +++ b/docs/index.rst @@ -5,6 +5,7 @@ Qiskit Nature Documentation .. toctree:: :maxdepth: 2 + Getting Started API Reference Tutorials Release Notes diff --git a/requirements-dev.txt b/requirements-dev.txt index 1ff4599d70..2807581510 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -8,7 +8,8 @@ stestr>=2.0.0 ddt>=1.2.0,!=1.4.0 reno>=3.2.0 Sphinx>=1.8.3,!=3.1.0,<4 -sphinx-tabs>=1.1.11 +sphinx-panels +sphinx-gallery sphinx-autodoc-typehints jupyter-sphinx discover