Qiskit / documentation

The documentation content home for https://docs.quantum.ibm.com.
https://docs.quantum.ibm.com
Apache License 2.0
37 stars 66 forks source link

Migrate Qiskit migration guides to MDX #213

Closed Eric-Arellano closed 11 months ago

Eric-Arellano commented 11 months ago

We now have the files copied over from open source, but we need to finish converting to MDX. The tool we used to convert from RST to MDX could only handle certain things.

Step 1: finish fixing cross-references

Sphinx uses cross-references like this:

:meth:`qiskit.my_module`
:meth:`~qiskit.my_module`
{meth}`qiskit.my_module`
{meth}`~qiskit.my_module`

We need to convert those to Markdown links. We have this script to automate the rewriting, but you need to manually set up the Conversion list because there have been enough edge cases of problems like the replacement not existing, or there being typos, or the replacement living in another repository. To run this script, you'll:

  1. Copy the file migrate-references.py to the root of your repository
  2. Add to the list CONVERSIONS. Tip that running black on the file makes it more readable
  3. python3 migrate-references.py
from __future__ import annotations

from dataclasses import dataclass
from pathlib import Path

GUIDES = [
    Path(f"docs/api/migration-guides/__migration-guide-{guide}.md.txt")
    for guide in ("algorithms", "opflow", "qi")
]

@dataclass(frozen=True)
class Conversion:
    qualified_name: str
    new_reference: str | None
    fixed_qualified_name: str | None = None

    def sphinx_values(self) -> set[str]:
        prefixes = []
        for role in ["class", "meth", "mod", "func"]:
            for tilde in ["", "~"]:
                for punctuation in [(":", ":"), ("{", "}")]:
                    initial, end = punctuation
                    # E.g.:
                    #   * :meth:`
                    #   * :meth:`~
                    #   * {meth}`
                    #   * {meth}`~
                    prefixes.append(f"{initial}{role}{end}`{tilde}")
        return {f"{prefix}{self.qualified_name}`" for prefix in prefixes}

    def new_value(self) -> str:
        name = self.fixed_qualified_name or self.qualified_name
        if self.new_reference is None:
            return f"`{name}`"
        return f"[`{name}`]({self.new_reference})"

    def validate_new_reference(self) -> None:
        """Check that the reference exists."""
        if self.new_reference is None or not self.new_reference.startswith("../"):
            return
        file_name = self.new_reference.split("#")[0]
        normalized_path = Path("docs/api/migration-guides").joinpath(f"{file_name}.md")
        if not normalized_path.exists():
            raise ValueError(
                f"The reference for `{self.qualified_name}` does not exist! {normalized_path}"
            )

CONVERSIONS = {
    # Classes
    Conversion("qiskit.opflow.OperatorBase", "../qiskit/qiskit.opflow.OperatorBase"),

    # API page does not exist
    Conversion(
        "qiskit.quantum_info.BaseOperator", None
    ),
}

REPLACEMENTS = {
    sphinx_value: conversion.new_value()
    for conversion in CONVERSIONS
    for sphinx_value in conversion.sphinx_values()
}

def update_line(line: str) -> str:
    for bad, new in REPLACEMENTS.items():
        line = line.replace(bad, new)
    return line

def main() -> None:
    for conversion in CONVERSIONS:
        conversion.validate_new_reference()

    for guide in GUIDES:
        result = [update_line(line) for line in guide.read_text().splitlines()]
        guide.write_text("\n".join(result) + "\n")

main()

See https://github.com/Qiskit/documentation/pull/233 for a bunch of examples of how to set up CONVERSIONS. Note the different sections to see examples of each different type of API, like how to use the # for a function vs a method, and how there are some non-Qiskit APIs.

There will be some cross-references to Qiskit APIs that aren't actually in the docs! This is because we never generated API docs for the code elements 😬 For these, please set new_reference to None, but add the bad code member in https://github.com/Qiskit/documentation/issues/238.

There are two parts to determining what goes into CONVERSIONS:

  1. Find the Sphinx cross reference, like :meth:. To do this, I've been manually searching for the key tags like :meth: and {meth}. It might be worth automating this search.

  2. Determine its new reference. You should search that the file actually exists, e.g. search for qiskit.circuit.QuantumInstance and you should find a file that ends in .md. The script will also validate that the file exists.

Step 2: see if soft launch works

Test if https://github.com/Qiskit/documentation/pull/237 works after step 1 is complete. Do this by checking in the live preview that the 3 URLs all render.

If they render, awesome! Merge the PR after getting review, especially that people like the file names I chose.

If they don't render, the errors are too cryptic to really understand, so probably not worth spending time trying to fix. You could make the PR simpler to only merge the files that are working; add .txt to the files still failing so they get ignored.

Step 3: convert list tables

There are a couple code blocks like this:

```{eval-rst}
.. list-table::

We want to convert these all to markdown tables. Look at https://qiskit.org/documentation/migration_guides/opflow_migration.html and friends to see how the final thing should render. This is where the soft launch from step 2 is useful because it will let you see how things render.

Step 4: convert dropdowns

We have a few dropdowns like this:

```{eval-rst}
.. dropdown:: Example 1: Applying an operator to a state
    :animate: fade-in-slide-down
Screenshot 2023-10-24 at 1 52 05 PM

We don't have an equivalent UI component in MDX. So for now, we need to switch these to be normal markdown. They'll be expanded, unfortunately.

To convert, I think you can remove the eval-rst header stuff and dedent the block. Consider using the tool https://rst-to-myst.readthedocs.io/en/latest/cli.html#rst2myst-convert to convert the blocks, at least most of them. I think you'd want to copy the code blocks over into temporary RST files, run the tool, then copy over the output back to the MDX file.

Check that these conversions didn't break the soft launch.

Step 5: convert code blocks

There are a few code blocks with testcode:: and testoutput::. These should simply be Markdown code blocks with Python as the language, or no language if the testoutput is clearly not Python. We use distinct code blocks for the code setup and the output. Look at the code blocks in https://qiskit.org/documentation/migration_guides/opflow_migration.html and friends for inspiration.

Step 6: final quality control

Check that things render how you expect in live preview. Then add the files to api/migration-guides/_toc.json and to api/migration-guides/index.mdx.

Check links like to qiskit.org.

Eric-Arellano commented 11 months ago

@javabster how should I handle this?

  1. For Opflow cross references, do we simply not link to anything since the API is going away?
  2. For Algorithms cross references, link to https://qiskit.org/ecosystem/algorithms/?
Eric-Arellano commented 11 months ago

Also @javabster it would be good to confirm we even want all 3 migration guides in OneDocs. It's a ton of work to migrate these, and I don't think it can very easily be automated.

My understanding is qiskit.org/documentation will go away, so it's not a solution to refer to an old version of qiskit.org/. But we could link to the RST file in GitHub? Granted, the RST file is harder for humans to read and the links won't be clickable to things. Maybe that's fine since people have hopefully already finished the migration? Although realistically many users won't migrate until going to Qiskit 1.0, so I don't think it's very realistic to think they won't need the guide.

I'm happy to do this ticket, I only want to double check it's worth investing the time.

Eric-Arellano commented 11 months ago

We talked it over and do want to keep the migration guides. They're important.

We're going to add back the API references for opflow and algorithms, even though they're deprecated. That will allow the cross references to work without issue.

kevinsung commented 11 months ago

It just occurred to me that qiskit.algorithms has been deprecated since version 0.25.0 and moved to an independent package: https://github.com/qiskit-community/qiskit-algorithms. So perhaps this migration guide should be moved to the documentation of that repository?

Eric-Arellano commented 11 months ago

So perhaps this migration guide should be moved to the documentation of that repository?

Abby M and I discussed and believe it's important the guide is in the API docs because it was a key part of Qiskit itself.