diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 4e71743..e887aed 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -10,6 +10,7 @@ exclude: > tests/test_solution/.*| tests/test_solution_references/.*| tests/test_hiddendirective/.*| + tests/test_gateddirective/.*| )$ repos: diff --git a/docs/source/img/gated-directive-example.png b/docs/source/img/gated-directive-example.png new file mode 100644 index 0000000..29eba4c Binary files /dev/null and b/docs/source/img/gated-directive-example.png differ diff --git a/docs/source/syntax.md b/docs/source/syntax.md index 2154242..7e373bd 100644 --- a/docs/source/syntax.md +++ b/docs/source/syntax.md @@ -140,6 +140,112 @@ factorial(4) _Source:_ [QuantEcon](https://python-programming.quantecon.org/functions.html#Exercise-1) +## Alternative Gated Syntax + +A restriction of MyST is that `code-cell` directives must be at the root level of the document for them to be executed. This maintains direct +compatility with the `jupyter notebook` and enables tools like `jupytext` to convert between `myst` and `ipynb` files. + +As a result **executable** `code-cell` directives cannot be nested inside of exercises or solution directives. + +The solution to this is to use the **gated syntax**. + +```{note} +This syntax can also be a convenient way of surrounding blocks of text that may include other directives that you wish +to include in an exercise or solution admonition. +``` + +### Basic Syntax + +````md +```{exercise-start} +:label: ex1 +``` + +```{code-cell} +# Some setup code that needs executing +``` + +and maybe you wish to add a figure + +```{figure} img/example.png +``` + +```{exercise-end} +``` +```` + +The `exercise-start` directive allows for he same options as core `exercise` directive. + +````md +```{solution-start} ex1 +``` + +```{code-cell} +# Solution Code +``` + +```{solution-end} +``` +```` + +```{warning} +If there are missing `-start` and `-end` directives, this will cause Sphinx to return an extension error, +alongside some helpful feedback to diagnose the issue in document structure. +``` + +### Example + +````md + +```{solution-start} exercise-1 +:label: solution-gated-1 +``` + +This is a solution to Exercise 1 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution + +```{solution-end} +``` + +```` + +will produce the following `solution` block in your `html` output. + +```{figure} img/gated-directive-example.png +``` + + ### Referencing Solutions You can refer to a solution using the `{ref}` role like: ```{ref}`my-solution` ``` the output of which depends on the attributes of the linked directive. If the linked directive is enumerable, the role will replace the solution reference with the linked directive type and its appropriate number like so: {ref}`my-solution`. @@ -266,16 +372,16 @@ Any specific directive can be hidden by introducing the `:hidden:` option. For e ````md ```{exercise} - :hidden: +:hidden: - This is a hidden exercise directive. +This is a hidden exercise directive. ``` ```` ```{exercise} - :hidden: +:hidden: - This is a hidden exercise directive. +This is a hidden exercise directive. ``` ### Remove All Solutions diff --git a/setup.py b/setup.py index e9c9918..7e10499 100644 --- a/setup.py +++ b/setup.py @@ -27,6 +27,7 @@ "beautifulsoup4", "myst-nb", "texsoup", + "matplotlib", ], "rtd": [ "sphinx>=3.0", diff --git a/sphinx_exercise/__init__.py b/sphinx_exercise/__init__.py index b560127..422e2b8 100644 --- a/sphinx_exercise/__init__.py +++ b/sphinx_exercise/__init__.py @@ -17,7 +17,14 @@ from sphinx.util import logging from sphinx.util.fileutil import copy_asset -from .directive import ExerciseDirective, SolutionDirective +from .directive import ( + ExerciseDirective, + ExerciseStartDirective, + ExerciseEndDirective, + SolutionDirective, + SolutionStartDirective, + SolutionEndDirective, +) from .nodes import ( exercise_node, visit_exercise_node, @@ -25,9 +32,12 @@ exercise_enumerable_node, visit_exercise_enumerable_node, depart_exercise_enumerable_node, + exercise_end_node, solution_node, visit_solution_node, depart_solution_node, + solution_start_node, + solution_end_node, is_extension_node, exercise_title, exercise_subtitle, @@ -37,6 +47,11 @@ visit_exercise_latex_number_reference, depart_exercise_latex_number_reference, ) +from .transforms import ( + CheckGatedDirectives, + MergeGatedSolutions, + MergeGatedExercises, +) from .post_transforms import ( ResolveTitlesInExercises, ResolveTitlesInSolutions, @@ -159,6 +174,9 @@ def setup(app: Sphinx) -> Dict[str, Any]: # Internal Title Nodes that don't need visit_ and depart_ methods # as they are resolved in post_transforms to docutil and sphinx nodes + app.add_node(exercise_end_node) + app.add_node(solution_start_node) + app.add_node(solution_end_node) app.add_node(exercise_title) app.add_node(exercise_subtitle) app.add_node(solution_title) @@ -173,7 +191,15 @@ def setup(app: Sphinx) -> Dict[str, Any]: ) app.add_directive("exercise", ExerciseDirective) + app.add_directive("exercise-start", ExerciseStartDirective) + app.add_directive("exercise-end", ExerciseEndDirective) app.add_directive("solution", SolutionDirective) + app.add_directive("solution-start", SolutionStartDirective) + app.add_directive("solution-end", SolutionEndDirective) + + app.add_transform(CheckGatedDirectives) + app.add_transform(MergeGatedExercises) + app.add_transform(MergeGatedSolutions) app.add_post_transform(UpdateReferencesToEnumerated) app.add_post_transform(ResolveTitlesInExercises) diff --git a/sphinx_exercise/directive.py b/sphinx_exercise/directive.py index 093df82..cfc3710 100644 --- a/sphinx_exercise/directive.py +++ b/sphinx_exercise/directive.py @@ -16,7 +16,10 @@ from .nodes import ( exercise_node, exercise_enumerable_node, + exercise_end_node, solution_node, + solution_start_node, + solution_end_node, exercise_title, exercise_subtitle, solution_title, @@ -103,6 +106,9 @@ def run(self) -> List[Node]: else: node = exercise_enumerable_node() + if self.name == "exercise-start": + node.gated = True + # Parse custom subtitle option if self.arguments != []: subtitle = exercise_subtitle() @@ -205,6 +211,7 @@ class : str, "class": directives.class_option, "hidden": directives.flag, } + solution_node = solution_node def run(self) -> List[Node]: @@ -250,7 +257,7 @@ def run(self) -> List[Node]: classes += self.options.get("class") # Construct Node - node = solution_node() + node = self.solution_node() node += title node += section node["target_label"] = target_label @@ -275,3 +282,146 @@ def run(self) -> List[Node]: return [] return [node] + + +# Gated Directives + + +class ExerciseStartDirective(ExerciseDirective): + """ + A gated directive for exercises + + .. exercise:: (optional) + :label: + :class: + :nonumber: + :hidden: + + This class is a child of ExerciseDirective so it supports + all the same options as the base exercise node + """ + + name = "exercise-start" + + def run(self): + # Initialise Gated Registry + if not hasattr(self.env, "sphinx_exercise_gated_registry"): + self.env.sphinx_exercise_gated_registry = {} + gated_registry = self.env.sphinx_exercise_gated_registry + docname = self.env.docname + if docname not in gated_registry: + gated_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + "type": "exercise", + } + gated_registry[self.env.docname]["start"].append(self.lineno) + gated_registry[self.env.docname]["sequence"].append("S") + gated_registry[self.env.docname]["msg"].append( + f"{self.name} at line: {self.lineno}" + ) + # Run Parent Methods + return super().run() + + +class ExerciseEndDirective(SphinxDirective): + """ + A simple gated directive to mark end of an exercise + + .. exercise-end:: + """ + + name = "exercise-end" + + def run(self): + # Initialise Gated Registry + if not hasattr(self.env, "sphinx_exercise_gated_registry"): + self.env.sphinx_exercise_gated_registry = {} + gated_registry = self.env.sphinx_exercise_gated_registry + docname = self.env.docname + if docname not in gated_registry: + gated_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + "type": "exercise", + } + gated_registry[self.env.docname]["end"].append(self.lineno) + gated_registry[self.env.docname]["sequence"].append("E") + gated_registry[self.env.docname]["msg"].append( + f"{self.name} at line: {self.lineno}" + ) + return [exercise_end_node()] + + +class SolutionStartDirective(SolutionDirective): + """ + A gated directive for solution + + .. solution-start:: + :label: + :class: + :hidden: + + This class is a child of SolutionDirective so it supports + all the same options as the base solution node + """ + + name = "solution-start" + solution_node = solution_start_node + + def run(self): + # Initialise Gated Registry (if required) + if not hasattr(self.env, "sphinx_exercise_gated_registry"): + self.env.sphinx_exercise_gated_registry = {} + gated_registry = self.env.sphinx_exercise_gated_registry + docname = self.env.docname + if docname not in gated_registry: + gated_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + "type": "solution", + } + gated_registry[self.env.docname]["start"].append(self.lineno) + gated_registry[self.env.docname]["sequence"].append("S") + gated_registry[self.env.docname]["msg"].append( + f"solution-start at line: {self.lineno}" + ) + # Run Parent Methods + return super().run() + + +class SolutionEndDirective(SphinxDirective): + """ + A simple gated directive to mark end of solution + + .. solution-end:: + """ + + name = "solution-end" + + def run(self): + # Initialise Gated Registry (if required) + if not hasattr(self.env, "sphinx_exercise_gated_registry"): + self.env.sphinx_exercise_gated_registry = {} + gated_registry = self.env.sphinx_exercise_gated_registry + docname = self.env.docname + if docname not in gated_registry: + gated_registry[docname] = { + "start": [], + "end": [], + "sequence": [], + "msg": [], + "type": "solution", + } + gated_registry[self.env.docname]["end"].append(self.lineno) + gated_registry[self.env.docname]["sequence"].append("E") + gated_registry[self.env.docname]["msg"].append( + f"solution-end at line: {self.lineno}" + ) + return [solution_end_node()] diff --git a/sphinx_exercise/nodes.py b/sphinx_exercise/nodes.py index 75fb2ae..8021e6f 100644 --- a/sphinx_exercise/nodes.py +++ b/sphinx_exercise/nodes.py @@ -23,17 +23,30 @@ class exercise_node(docutil_nodes.Admonition, docutil_nodes.Element): - pass + gated = False class exercise_enumerable_node(docutil_nodes.Admonition, docutil_nodes.Element): + gated = False resolved_title = False +class exercise_end_node(docutil_nodes.Admonition, docutil_nodes.Element): + pass + + class solution_node(docutil_nodes.Admonition, docutil_nodes.Element): resolved_title = False +class solution_start_node(docutil_nodes.Admonition, docutil_nodes.Element): + resolved_title = False + + +class solution_end_node(docutil_nodes.Admonition, docutil_nodes.Element): + resolved_title = False # TODO: is this required? + + class exercise_title(docutil_nodes.title): def default_title(self): title_text = self.children[0].astext() diff --git a/sphinx_exercise/transforms.py b/sphinx_exercise/transforms.py new file mode 100644 index 0000000..cb653f8 --- /dev/null +++ b/sphinx_exercise/transforms.py @@ -0,0 +1,183 @@ +import re +import docutils + +from sphinx.transforms import SphinxTransform +from sphinx.util import logging +from sphinx.errors import ExtensionError + +# from sphinx.errors import ExtensionError + +from .nodes import ( + exercise_node, + exercise_enumerable_node, + exercise_end_node, + solution_node, + solution_start_node, + solution_end_node, +) + +logger = logging.getLogger(__name__) + + +class CheckGatedDirectives(SphinxTransform): + """ + This transform checks the structure of the gated solutions + to flag any errors in input + """ + + default_priority = 1 + + def check_structure(self, registry): + """ Check Structure of the Gated Registry""" + error = False + docname = self.env.docname + if docname in registry: + start = registry[docname]["start"] + end = registry[docname]["end"] + sequence = "".join(registry[docname]["sequence"]) + structure = "\n ".join(registry[docname]["msg"]) + nodetype = registry[docname]["type"] + if len(start) > len(end): + msg = f"The document ({docname}) is missing a {nodetype}-end directive\n {structure}" # noqa: E501 + logger.error(msg) + error = True + if len(start) < len(end): + msg = f"The document ({docname}) is missing a {nodetype}-start directive\n {structure}" # noqa: E501 + logger.error(msg) + error = True + if len(start) == len(end): + groups = re.findall("(SE)", sequence) + if len(groups) != len(start): + msg = f"The document ({docname}) contains nested {nodetype}-start and {nodetype}-end directives\n {structure}" # noqa: E501 + logger.error(msg) + error = True + if error: + msg = "[sphinx-exercise] An error has occured when parsing gated directives.\nPlease check warning messages above" # noqa: E501 + raise ExtensionError(message=msg) + + def apply(self): + # Check structure of all -start and -end nodes + if hasattr(self.env, "sphinx_exercise_gated_registry"): + self.check_structure(self.env.sphinx_exercise_gated_registry) + + +class MergeGatedSolutions(SphinxTransform): + """ + Transform Gated Directives into single unified + Directives in the Sphinx Abstract Syntax Tree + + Note: The CheckGatedSolutions Transform should ensure the + structure of the gated directives is correct before + this transform is run. + """ + + default_priority = 10 + + def find_nodes(self, label, node): + parent_node = node.parent + parent_start, parent_end = None, None + for idx1, child in enumerate(parent_node.children): + if isinstance(child, solution_start_node) and label == child.get("label"): + parent_start = idx1 + for idx2, child2 in enumerate(parent_node.children[parent_start:]): + if isinstance(child2, solution_end_node): + parent_end = idx1 + idx2 + break + break + return parent_start, parent_end + + def apply(self): + # Process all matching solution-start and solution-end nodes + for node in self.document.traverse(solution_start_node): + label = node.get("label") + parent_start, parent_end = self.find_nodes(label, node) + if not parent_end: + continue + parent = node.parent + # Rebuild Node as a Solution Node + new_node = solution_node() + new_node.attributes = node.attributes + # Update Attributes + new_node["classes"] = [ + attr.replace("solution-start", "solution") + for attr in node.attributes["classes"] + ] + new_node["type"] = "solution" + new_node.parent = node.parent + for child in node.children: + if type(child) is docutils.nodes.section: + pass + else: + new_node += child + # Collect nodes attached to the Parent Node until :solution-end: + content = docutils.nodes.section( + ids="solution-content" + ) # TODO: should id be classes? + for child in parent.children[parent_start + 1 : parent_end]: + content += child + new_node += content + # Replace :solution-start: with new solution node + node.replace_self(new_node) + # Clean up Parent Node including :solution-end: + for child in parent.children[parent_start + 1 : parent_end + 1]: + parent.remove(child) + + +class MergeGatedExercises(SphinxTransform): + """ + Transform Gated Exercise Directives into single unified + Directives in the Sphinx Abstract Syntax Tree + + Note: The CheckGatedDirectives Transform should ensure the + structure of the gated directives is correct before + this transform is run. + """ + + default_priority = 10 + + def find_nodes(self, label, node): + parent_node = node.parent + parent_start, parent_end = None, None + for idx1, child in enumerate(parent_node.children): + if isinstance( + child, (exercise_node, exercise_enumerable_node) + ) and label == child.get("label"): + parent_start = idx1 + for idx2, child2 in enumerate(parent_node.children[parent_start:]): + if isinstance(child2, exercise_end_node): + parent_end = idx1 + idx2 + break + break + return parent_start, parent_end + + def merge_nodes(self, node): + label = node.get("label") + parent_start, parent_end = self.find_nodes(label, node) + if not parent_end: + return + parent = node.parent + # Use Current Node and remove "-start" from class names and type + updated_classes = [ + cls.replace("-start", "") for cls in node.attributes["classes"] + ] + node.attributes["classes"] = updated_classes + node.attributes["type"] = node.attributes["type"].replace("-start", "") + # Attach content to section + content = node.children[-1] + for child in parent.children[parent_start + 1 : parent_end]: + content += child + # Clean up Parent Node including :exercise-end: + for child in parent.children[parent_start + 1 : parent_end + 1]: + parent.remove(child) + + def apply(self): + # Process all matching exercise and exercise-enumerable (gated=True) + # and exercise-end nodes + for node in self.document.traverse(exercise_node): + if node.gated: + self.merge_nodes(node) + node.gated = False + for node in self.document.traverse(exercise_enumerable_node): + if node.gated: + self.merge_nodes(node) + node.gated = False diff --git a/tests/books/test-gateddirective/Makefile b/tests/books/test-gateddirective/Makefile new file mode 100644 index 0000000..ed88099 --- /dev/null +++ b/tests/books/test-gateddirective/Makefile @@ -0,0 +1,20 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = build + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help Makefile + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +%: Makefile + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) diff --git a/tests/books/test-gateddirective/conf.py b/tests/books/test-gateddirective/conf.py new file mode 100644 index 0000000..d81b3c2 --- /dev/null +++ b/tests/books/test-gateddirective/conf.py @@ -0,0 +1,56 @@ +# Configuration file for the Sphinx documentation builder. +# +# This file only contains a selection of the most common options. For a full +# list see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Path setup -------------------------------------------------------------- + +# If extensions (or modules to document with autodoc) are in another directory, +# 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('.')) + + +# -- Project information ----------------------------------------------------- + +project = "sphinx-exercise" +copyright = "2020-2021, Executable Book Developers" +author = "Executable Book Developers" + + +# -- General configuration --------------------------------------------------- + +# Add any Sphinx extension module names here, as strings. They can be +# extensions coming with Sphinx (named 'sphinx.ext.*') or your custom +# ones. +extensions = ["sphinx_exercise", "myst_nb"] + +# Add any paths that contain templates here, relative to this directory. +templates_path = ["_templates"] + +# List of patterns, relative to source directory, that match files and +# directories to ignore when looking for source files. +# This pattern also affects html_static_path and html_extra_path. +exclude_patterns = [ + "build", + "_build", + "exercise_errors_[1,2,3]*", + "solution_errors_[1,2,3]*", +] + + +# -- Options for HTML output ------------------------------------------------- + +# The theme to use for HTML and HTML Help pages. See the documentation for +# a list of builtin themes. +# +html_theme = "sphinx_book_theme" + +# Add any paths that contain custom static files (such as style sheets) here, +# relative to this directory. They are copied after the builtin static files, +# so a file named "default.css" will overwrite the builtin "default.css". +# html_static_path = ['_static'] diff --git a/tests/books/test-gateddirective/exercise-gated.md b/tests/books/test-gateddirective/exercise-gated.md new file mode 100644 index 0000000..944ea38 --- /dev/null +++ b/tests/books/test-gateddirective/exercise-gated.md @@ -0,0 +1,39 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Gated Exercises + +Some Gated reference exercises + +```{exercise-start} +:label: gated-exercise-1 +``` + +Replicate this figure using matplotlib + +```{figure} sphx_glr_cohere_001_2_0x.png +``` + +```{exercise-end} +``` + +and another version with a title embedded + + +```{exercise-start} Replicate Matplotlib Plot +:label: gated-exercise-2 +``` + +```{figure} sphx_glr_cohere_001_2_0x.png +``` + +```{exercise-end} +``` diff --git a/tests/books/test-gateddirective/exercise.md b/tests/books/test-gateddirective/exercise.md new file mode 100644 index 0000000..7d3880c --- /dev/null +++ b/tests/books/test-gateddirective/exercise.md @@ -0,0 +1,32 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Non-Gated Exercises + +Some reference exercises + +````{exercise} +:label: exercise-1 + +Replicate this figure using matplotlib + +```{figure} sphx_glr_cohere_001_2_0x.png +``` +```` + +and another version with a title embedded + +````{exercise} Replicate Matplotlib Plot +:label: exercise-2 + +```{figure} sphx_glr_cohere_001_2_0x.png +``` +```` diff --git a/tests/books/test-gateddirective/exercise_errors_1.md b/tests/books/test-gateddirective/exercise_errors_1.md new file mode 100644 index 0000000..cdaa8f3 --- /dev/null +++ b/tests/books/test-gateddirective/exercise_errors_1.md @@ -0,0 +1,27 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Exercise Errors 1 + +Some malformed directives + +## Missing `-end` directive + +A solution using the gated directive + +```{exercise-start} +:label: error-exercise-1 +``` + +Replicate this figure using matplotlib + +```{figure} sphx_glr_cohere_001_2_0x.png +``` diff --git a/tests/books/test-gateddirective/exercise_errors_2.md b/tests/books/test-gateddirective/exercise_errors_2.md new file mode 100644 index 0000000..cdf47d9 --- /dev/null +++ b/tests/books/test-gateddirective/exercise_errors_2.md @@ -0,0 +1,24 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Exercise Errors 2 + +Some malformed directives + +## Missing `-start` directive + +Replicate this figure using matplotlib + +```{figure} sphx_glr_cohere_001_2_0x.png +``` + +```{exercise-end} +``` diff --git a/tests/books/test-gateddirective/exercise_errors_3.md b/tests/books/test-gateddirective/exercise_errors_3.md new file mode 100644 index 0000000..c8e00f0 --- /dev/null +++ b/tests/books/test-gateddirective/exercise_errors_3.md @@ -0,0 +1,26 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Errors 3 + +## Incorrect Nesting of Gated Exercise Directives + +```{exercise-start} +``` + +```{exercise-start} +``` + +```{exercise-end} +``` + +```{exercise-end} +``` diff --git a/tests/books/test-gateddirective/index.md b/tests/books/test-gateddirective/index.md new file mode 100644 index 0000000..7bd5a4f --- /dev/null +++ b/tests/books/test-gateddirective/index.md @@ -0,0 +1,22 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Gated Directives + +```{toctree} +:hidden: true +:maxdepth: 1 + +exercise +solution-exercise +exercise-gated +solution-exercise-gated +``` diff --git a/tests/books/test-gateddirective/make.bat b/tests/books/test-gateddirective/make.bat new file mode 100644 index 0000000..2c0764f --- /dev/null +++ b/tests/books/test-gateddirective/make.bat @@ -0,0 +1,35 @@ +@ECHO OFF + +pushd %~dp0 + +REM Command file for Sphinx documentation + +if "%SPHINXBUILD%" == "" ( + set SPHINXBUILD=sphinx-build +) +set SOURCEDIR=. +set BUILDDIR=build + +if "%1" == "" goto help + +%SPHINXBUILD% >NUL 2>NUL +if errorlevel 9009 ( + echo. + echo.The 'sphinx-build' command was not found. Make sure you have Sphinx + echo.installed, then set the SPHINXBUILD environment variable to point + echo.to the full path of the 'sphinx-build' executable. Alternatively you + echo.may add the Sphinx directory to PATH. + echo. + echo.If you don't have Sphinx installed, grab it from + echo.http://sphinx-doc.org/ + exit /b 1 +) + +%SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% +goto end + +:help +%SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% + +:end +popd diff --git a/tests/books/test-gateddirective/solution-exercise-gated.md b/tests/books/test-gateddirective/solution-exercise-gated.md new file mode 100644 index 0000000..7fb42c1 --- /dev/null +++ b/tests/books/test-gateddirective/solution-exercise-gated.md @@ -0,0 +1,104 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Gated Solutions to exercise-gated.md + +A solution using the gated directive + +```{solution-start} gated-exercise-1 +:label: gated-exercise-solution-1 +``` + +This is a solution to Gated Exercise 1 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution + +```{solution-end} +``` + +and then a solution to {ref}`gated-exercise-2` + +A solution using the gated directive + +```{solution-start} gated-exercise-2 +:label: gated-exercise-solution-2 +``` + +This is a solution to Gated Exercise 2 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution + +```{solution-end} +``` + +## References to Solutions + +This is a reference to {ref}`gated-exercise-solution-1` diff --git a/tests/books/test-gateddirective/solution-exercise.md b/tests/books/test-gateddirective/solution-exercise.md new file mode 100644 index 0000000..176bb6f --- /dev/null +++ b/tests/books/test-gateddirective/solution-exercise.md @@ -0,0 +1,104 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Gated Solutions to exercise.md + +A solution using the gated directive + +```{solution-start} exercise-1 +:label: solution-gated-1 +``` + +This is a solution to Non-Gated Exercise 1 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution + +```{solution-end} +``` + +and a solution to {ref}`exercise-2` + +```{solution-start} exercise-2 +:label: solution-gated-2 +``` + +This is a solution to Non-Gated Exercise 1 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution + +```{solution-end} +``` + +## References + +This is a reference to {ref}`solution-gated-1` + +This is a reference to {ref}`solution-gated-2` diff --git a/tests/books/test-gateddirective/solution_errors_1.md b/tests/books/test-gateddirective/solution_errors_1.md new file mode 100644 index 0000000..b43af8f --- /dev/null +++ b/tests/books/test-gateddirective/solution_errors_1.md @@ -0,0 +1,56 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Solution Errors 1 + +Some malformed directives + +## Missing `-end` directive + +A solution using the gated directive + +```{solution-start} exercise-1 +:label: solution-gated-1 +``` + +This is a solution to Exercise 1 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution diff --git a/tests/books/test-gateddirective/solution_errors_2.md b/tests/books/test-gateddirective/solution_errors_2.md new file mode 100644 index 0000000..a55f754 --- /dev/null +++ b/tests/books/test-gateddirective/solution_errors_2.md @@ -0,0 +1,55 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Solution Errors 2 + +Some malformed directives + +## Missing `-start` directive + +A solution using the gated directive + +This is a solution to Exercise 1 + +```{code-cell} python3 +import numpy as np +import matplotlib.pyplot as plt + +# Fixing random state for reproducibility +np.random.seed(19680801) + +dt = 0.01 +t = np.arange(0, 30, dt) +nse1 = np.random.randn(len(t)) # white noise 1 +nse2 = np.random.randn(len(t)) # white noise 2 + +# Two signals with a coherent part at 10Hz and a random part +s1 = np.sin(2 * np.pi * 10 * t) + nse1 +s2 = np.sin(2 * np.pi * 10 * t) + nse2 + +fig, axs = plt.subplots(2, 1) +axs[0].plot(t, s1, t, s2) +axs[0].set_xlim(0, 2) +axs[0].set_xlabel('time') +axs[0].set_ylabel('s1 and s2') +axs[0].grid(True) + +cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) +axs[1].set_ylabel('coherence') + +fig.tight_layout() +plt.show() +``` + +With some follow up text to the solution + +```{solution-end} +``` diff --git a/tests/books/test-gateddirective/solution_errors_3.md b/tests/books/test-gateddirective/solution_errors_3.md new file mode 100644 index 0000000..7149ef9 --- /dev/null +++ b/tests/books/test-gateddirective/solution_errors_3.md @@ -0,0 +1,26 @@ +--- +jupytext: + text_representation: + extension: .md + format_name: myst +kernelspec: + display_name: Python 3 + language: python + name: python3 +--- + +# Solution Errors 3 + +## Incorrect Nesting of Gated Directives + +```{solution-start} solution1 +``` + +```{solution-start} solution2 +``` + +```{solution-end} +``` + +```{solution-end} +``` diff --git a/tests/books/test-gateddirective/sphx_glr_cohere_001_2_0x.png b/tests/books/test-gateddirective/sphx_glr_cohere_001_2_0x.png new file mode 100644 index 0000000..4bcb373 Binary files /dev/null and b/tests/books/test-gateddirective/sphx_glr_cohere_001_2_0x.png differ diff --git a/tests/test_gateddirective.py b/tests/test_gateddirective.py new file mode 100644 index 0000000..49b47be --- /dev/null +++ b/tests/test_gateddirective.py @@ -0,0 +1,200 @@ +import os +import pytest +import shutil +from bs4 import BeautifulSoup +from sphinx.errors import ExtensionError + +from sphinx.testing.util import strip_escseq + + +@pytest.mark.sphinx("html", testroot="gateddirective") +@pytest.mark.parametrize("docname", ["exercise-gated.html"]) +def test_gated_exercise_build(app, docname, file_regression): + app.build() + path_to_html = app.outdir / docname + # get content markup + soup = BeautifulSoup(path_to_html.read_text(encoding="utf8"), "html.parser") + exercise_directives = soup.select("div.exercise") + for idx, ed in enumerate(exercise_directives): + basename = docname.split(".")[0] + f"-{idx}" + file_regression.check(str(ed), basename=basename, extension=".html") + + +@pytest.mark.sphinx("html", testroot="gateddirective") +@pytest.mark.parametrize("docname", ["exercise-gated"]) +def test_gated_exercise_doctree(app, docname, get_sphinx_app_doctree): + # Clean Up Build Directory from Previous Runs + build_dir = "/".join(app.outdir.split("/")[:-1]) + shutil.rmtree(build_dir) + # Test + app.build() + get_sphinx_app_doctree( + app, + docname, + resolve=False, + regress=True, + ) + + +@pytest.mark.sphinx("html", testroot="gateddirective") +@pytest.mark.parametrize( + "docname", ["solution-exercise.html", "solution-exercise-gated.html"] +) +def test_gated_solution_build(app, docname, file_regression): + app.build() + path_to_html = app.outdir / docname + # get content markup + soup = BeautifulSoup(path_to_html.read_text(encoding="utf8"), "html.parser") + solution_directives = soup.select("div.solution") + for idx, sd in enumerate(solution_directives): + basename = docname.split(".")[0] + f"-{idx}" + file_regression.check(str(sd), basename=basename, extension=".html") + + +@pytest.mark.sphinx("html", testroot="gateddirective") +@pytest.mark.parametrize("docname", ["solution-exercise", "solution-exercise-gated"]) +def test_gated_solution_doctree(app, docname, get_sphinx_app_doctree): + # Clean Up Build Directory from Previous Runs + build_dir = "/".join(app.outdir.split("/")[:-1]) + shutil.rmtree(build_dir) + # Test + app.build() + get_sphinx_app_doctree( + app, + docname, + resolve=False, + regress=True, + ) + + +# Errors + + +def getwarning(warnings): + return strip_escseq(warnings.getvalue().replace(os.sep, "/")) + + +@pytest.mark.sphinx("html", testroot="gateddirective") +def test_gated_exercise_errors_1(app, warning): + app.config.exclude_patterns = [ + "build", + "_build", + "solution_errors_[1,2,3]*", + "exercise_errors_[2,3]*", + ] + try: + app.build() + except ExtensionError: + warnings = getwarning(warning) + warn1 = "is missing a exercise-end directive" + warn2 = "exercise-start at line: 20" + for warn in [warn1, warn2]: + assert warn in warnings + assert True + else: + assert False + + +@pytest.mark.sphinx("html", testroot="gateddirective") +def test_gated_exercise_errors_2(app, warning): + app.config.exclude_patterns = [ + "build", + "_build", + "solution_errors_[1,2,3]*", + "exercise_errors_[1,3]*", + ] + try: + app.build() + except ExtensionError: + warnings = getwarning(warning) + warn1 = "is missing a exercise-start directive" + warn2 = "exercise-end at line: 23" + for warn in [warn1, warn2]: + assert warn in warnings + assert True + else: + assert False + + +@pytest.mark.sphinx("html", testroot="gateddirective") +def test_gated_exercise_errors_3(app, warning): + app.config.exclude_patterns = [ + "build", + "_build", + "solution_errors_[1,2,3]*", + "exercise_errors_[1,2]*", + ] + try: + app.build() + except ExtensionError: + warnings = getwarning(warning) + warn1 = "contains nested exercise-start and exercise-end directives" + warn2 = "exercise-start at line: 16\n exercise-start at line: 19\n exercise-end at line: 22\n exercise-end at line: 25" # noqa: #501 + for warn in [warn1, warn2]: + assert warn in warnings + assert True + else: + assert False + + +@pytest.mark.sphinx("html", testroot="gateddirective") +def test_gated_solution_errors_1(app, warning): + app.config.exclude_patterns = [ + "build", + "_build", + "exercise_errors_[1,2,3]*", + "solution_errors_[2,3]*", + ] + try: + app.build() + except ExtensionError: + warnings = getwarning(warning) + warn1 = "is missing a solution-end directive" + warn2 = "solution-start at line: 20" + for warn in [warn1, warn2]: + assert warn in warnings + assert True + else: + assert False + + +@pytest.mark.sphinx("html", testroot="gateddirective") +def test_gated_solution_errors_2(app, warning): + app.config.exclude_patterns = [ + "build", + "_build", + "exercise_errors_[1,2,3]*", + "solution_errors_[1,3]*", + ] + try: + app.build() + except ExtensionError: + warnings = getwarning(warning) + warn1 = "is missing a solution-start directive" + warn2 = "solution-end at line: 54" + for warn in [warn1, warn2]: + assert warn in warnings + assert True + else: + assert False + + +@pytest.mark.sphinx("html", testroot="gateddirective") +def test_gated_solution_errors_3(app, warning): + app.config.exclude_patterns = [ + "build", + "_build", + "exercise_errors_[1,2,3]*", + "solution_errors_[1,2]*", + ] + try: + app.build() + except ExtensionError: + warnings = getwarning(warning) + warn1 = "contains nested solution-start and solution-end directives" + warn2 = "solution-start at line: 16\n solution-start at line: 19\n solution-end at line: 22\n solution-end at line: 25" # noqa: #501 + for warn in [warn1, warn2]: + assert warn in warnings + assert True + else: + assert False diff --git a/tests/test_gateddirective/exercise-gated-0.html b/tests/test_gateddirective/exercise-gated-0.html new file mode 100644 index 0000000..b3cbfea --- /dev/null +++ b/tests/test_gateddirective/exercise-gated-0.html @@ -0,0 +1,9 @@ +
+

Exercise 3

+
+

Replicate this figure using matplotlib

+
+_images/sphx_glr_cohere_001_2_0x.png +
+
+
\ No newline at end of file diff --git a/tests/test_gateddirective/exercise-gated-1.html b/tests/test_gateddirective/exercise-gated-1.html new file mode 100644 index 0000000..a2d342f --- /dev/null +++ b/tests/test_gateddirective/exercise-gated-1.html @@ -0,0 +1,8 @@ +
+

Exercise 4 (Replicate Matplotlib Plot)

+
+
+_images/sphx_glr_cohere_001_2_0x.png +
+
+
\ No newline at end of file diff --git a/tests/test_gateddirective/exercise-gated.xml b/tests/test_gateddirective/exercise-gated.xml new file mode 100644 index 0000000..64cbace --- /dev/null +++ b/tests/test_gateddirective/exercise-gated.xml @@ -0,0 +1,24 @@ + +
+ + Gated Exercises + <paragraph> + Some Gated reference exercises + <exercise_enumerable_node classes="exercise" docname="exercise-gated" hidden="False" ids="gated-exercise-1" label="gated-exercise-1" names="gated-exercise-1" serial_number="0" title="Exercise" type="exercise"> + <exercise_title> + Exercise + <section ids="exercise-content"> + <paragraph> + Replicate this figure using matplotlib + <figure> + <image candidates="{'*': 'sphx_glr_cohere_001_2_0x.png'}" uri="sphx_glr_cohere_001_2_0x.png"> + <paragraph> + and another version with a title embedded + <exercise_enumerable_node classes="exercise" docname="exercise-gated" hidden="False" ids="gated-exercise-2" label="gated-exercise-2" names="gated-exercise-2" serial_number="1" title="Exercise" type="exercise"> + <exercise_title> + Exercise + <exercise_subtitle> + Replicate Matplotlib Plot + <section ids="exercise-content"> + <figure> + <image candidates="{'*': 'sphx_glr_cohere_001_2_0x.png'}" uri="sphx_glr_cohere_001_2_0x.png"> diff --git a/tests/test_gateddirective/solution-exercise-0.html b/tests/test_gateddirective/solution-exercise-0.html new file mode 100644 index 0000000..8c47684 --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-0.html @@ -0,0 +1,43 @@ +<div class="solution admonition" id="solution-gated-1"> +<p class="admonition-title">Solution to<a class="reference internal" href="exercise.html#exercise-1"> Exercise 1</a></p> +<div class="section" id="s"> +<span id="o"></span><span id="l"></span><span id="u"></span><span id="t"></span><span id="i"></span><span id="o"></span><span id="n"></span><span id="-"></span><span id="c"></span><span id="o"></span><span id="n"></span><span id="t"></span><span id="e"></span><span id="n"></span><span id="t"></span><p>This is a solution to Non-Gated Exercise 1</p> +<div class="cell docutils container"> +<div class="cell_input docutils container"> +<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> +<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span> + +<span class="c1"># Fixing random state for reproducibility</span> +<span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">19680801</span><span class="p">)</span> + +<span class="n">dt</span> <span class="o">=</span> <span class="mf">0.01</span> +<span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">nse1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 1</span> +<span class="n">nse2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 2</span> + +<span class="c1"># Two signals with a coherent part at 10Hz and a random part</span> +<span class="n">s1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse1</span> +<span class="n">s2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse2</span> + +<span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s1">'time'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'s1 and s2'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> + +<span class="n">cxy</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">cohere</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mf">1.</span> <span class="o">/</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'coherence'</span><span class="p">)</span> + +<span class="n">fig</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span> +<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> +</pre></div> +</div> +</div> +<div class="cell_output docutils container"> +<img alt="_images/solution-exercise_1_0.png" src="_images/solution-exercise_1_0.png"/> +</div> +</div> +<p>With some follow up text to the solution</p> +</div> +</div> \ No newline at end of file diff --git a/tests/test_gateddirective/solution-exercise-1.html b/tests/test_gateddirective/solution-exercise-1.html new file mode 100644 index 0000000..e78f978 --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-1.html @@ -0,0 +1,43 @@ +<div class="solution admonition" id="solution-gated-2"> +<p class="admonition-title">Solution to<a class="reference internal" href="exercise.html#exercise-2"> Exercise 2 (Replicate Matplotlib Plot)</a></p> +<div class="section" id="s"> +<span id="o"></span><span id="l"></span><span id="u"></span><span id="t"></span><span id="i"></span><span id="o"></span><span id="n"></span><span id="-"></span><span id="c"></span><span id="o"></span><span id="n"></span><span id="t"></span><span id="e"></span><span id="n"></span><span id="t"></span><p>This is a solution to Non-Gated Exercise 1</p> +<div class="cell docutils container"> +<div class="cell_input docutils container"> +<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> +<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span> + +<span class="c1"># Fixing random state for reproducibility</span> +<span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">19680801</span><span class="p">)</span> + +<span class="n">dt</span> <span class="o">=</span> <span class="mf">0.01</span> +<span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">nse1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 1</span> +<span class="n">nse2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 2</span> + +<span class="c1"># Two signals with a coherent part at 10Hz and a random part</span> +<span class="n">s1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse1</span> +<span class="n">s2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse2</span> + +<span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s1">'time'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'s1 and s2'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> + +<span class="n">cxy</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">cohere</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mf">1.</span> <span class="o">/</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'coherence'</span><span class="p">)</span> + +<span class="n">fig</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span> +<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> +</pre></div> +</div> +</div> +<div class="cell_output docutils container"> +<img alt="_images/solution-exercise_3_0.png" src="_images/solution-exercise_3_0.png"/> +</div> +</div> +<p>With some follow up text to the solution</p> +</div> +</div> \ No newline at end of file diff --git a/tests/test_gateddirective/solution-exercise-gated-0.html b/tests/test_gateddirective/solution-exercise-gated-0.html new file mode 100644 index 0000000..e80578e --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-gated-0.html @@ -0,0 +1,43 @@ +<div class="solution admonition" id="gated-exercise-solution-1"> +<p class="admonition-title">Solution to<a class="reference internal" href="exercise-gated.html#gated-exercise-1"> Exercise 3</a></p> +<div class="section" id="s"> +<span id="o"></span><span id="l"></span><span id="u"></span><span id="t"></span><span id="i"></span><span id="o"></span><span id="n"></span><span id="-"></span><span id="c"></span><span id="o"></span><span id="n"></span><span id="t"></span><span id="e"></span><span id="n"></span><span id="t"></span><p>This is a solution to Gated Exercise 1</p> +<div class="cell docutils container"> +<div class="cell_input docutils container"> +<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> +<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span> + +<span class="c1"># Fixing random state for reproducibility</span> +<span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">19680801</span><span class="p">)</span> + +<span class="n">dt</span> <span class="o">=</span> <span class="mf">0.01</span> +<span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">nse1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 1</span> +<span class="n">nse2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 2</span> + +<span class="c1"># Two signals with a coherent part at 10Hz and a random part</span> +<span class="n">s1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse1</span> +<span class="n">s2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse2</span> + +<span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s1">'time'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'s1 and s2'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> + +<span class="n">cxy</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">cohere</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mf">1.</span> <span class="o">/</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'coherence'</span><span class="p">)</span> + +<span class="n">fig</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span> +<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> +</pre></div> +</div> +</div> +<div class="cell_output docutils container"> +<img alt="_images/solution-exercise-gated_1_0.png" src="_images/solution-exercise-gated_1_0.png"/> +</div> +</div> +<p>With some follow up text to the solution</p> +</div> +</div> \ No newline at end of file diff --git a/tests/test_gateddirective/solution-exercise-gated-1.html b/tests/test_gateddirective/solution-exercise-gated-1.html new file mode 100644 index 0000000..19d688e --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-gated-1.html @@ -0,0 +1,43 @@ +<div class="solution admonition" id="gated-exercise-solution-2"> +<p class="admonition-title">Solution to<a class="reference internal" href="exercise-gated.html#gated-exercise-2"> Exercise 4 (Replicate Matplotlib Plot)</a></p> +<div class="section" id="s"> +<span id="o"></span><span id="l"></span><span id="u"></span><span id="t"></span><span id="i"></span><span id="o"></span><span id="n"></span><span id="-"></span><span id="c"></span><span id="o"></span><span id="n"></span><span id="t"></span><span id="e"></span><span id="n"></span><span id="t"></span><p>This is a solution to Gated Exercise 2</p> +<div class="cell docutils container"> +<div class="cell_input docutils container"> +<div class="highlight-ipython3 notranslate"><div class="highlight"><pre><span></span><span class="kn">import</span> <span class="nn">numpy</span> <span class="k">as</span> <span class="nn">np</span> +<span class="kn">import</span> <span class="nn">matplotlib.pyplot</span> <span class="k">as</span> <span class="nn">plt</span> + +<span class="c1"># Fixing random state for reproducibility</span> +<span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">seed</span><span class="p">(</span><span class="mi">19680801</span><span class="p">)</span> + +<span class="n">dt</span> <span class="o">=</span> <span class="mf">0.01</span> +<span class="n">t</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">arange</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">30</span><span class="p">,</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">nse1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 1</span> +<span class="n">nse2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">random</span><span class="o">.</span><span class="n">randn</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">t</span><span class="p">))</span> <span class="c1"># white noise 2</span> + +<span class="c1"># Two signals with a coherent part at 10Hz and a random part</span> +<span class="n">s1</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse1</span> +<span class="n">s2</span> <span class="o">=</span> <span class="n">np</span><span class="o">.</span><span class="n">sin</span><span class="p">(</span><span class="mi">2</span> <span class="o">*</span> <span class="n">np</span><span class="o">.</span><span class="n">pi</span> <span class="o">*</span> <span class="mi">10</span> <span class="o">*</span> <span class="n">t</span><span class="p">)</span> <span class="o">+</span> <span class="n">nse2</span> + +<span class="n">fig</span><span class="p">,</span> <span class="n">axs</span> <span class="o">=</span> <span class="n">plt</span><span class="o">.</span><span class="n">subplots</span><span class="p">(</span><span class="mi">2</span><span class="p">,</span> <span class="mi">1</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">plot</span><span class="p">(</span><span class="n">t</span><span class="p">,</span> <span class="n">s1</span><span class="p">,</span> <span class="n">t</span><span class="p">,</span> <span class="n">s2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlim</span><span class="p">(</span><span class="mi">0</span><span class="p">,</span> <span class="mi">2</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_xlabel</span><span class="p">(</span><span class="s1">'time'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'s1 and s2'</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">0</span><span class="p">]</span><span class="o">.</span><span class="n">grid</span><span class="p">(</span><span class="kc">True</span><span class="p">)</span> + +<span class="n">cxy</span><span class="p">,</span> <span class="n">f</span> <span class="o">=</span> <span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">cohere</span><span class="p">(</span><span class="n">s1</span><span class="p">,</span> <span class="n">s2</span><span class="p">,</span> <span class="mi">256</span><span class="p">,</span> <span class="mf">1.</span> <span class="o">/</span> <span class="n">dt</span><span class="p">)</span> +<span class="n">axs</span><span class="p">[</span><span class="mi">1</span><span class="p">]</span><span class="o">.</span><span class="n">set_ylabel</span><span class="p">(</span><span class="s1">'coherence'</span><span class="p">)</span> + +<span class="n">fig</span><span class="o">.</span><span class="n">tight_layout</span><span class="p">()</span> +<span class="n">plt</span><span class="o">.</span><span class="n">show</span><span class="p">()</span> +</pre></div> +</div> +</div> +<div class="cell_output docutils container"> +<img alt="_images/solution-exercise-gated_3_0.png" src="_images/solution-exercise-gated_3_0.png"/> +</div> +</div> +<p>With some follow up text to the solution</p> +</div> +</div> \ No newline at end of file diff --git a/tests/test_gateddirective/solution-exercise-gated.xml b/tests/test_gateddirective/solution-exercise-gated.xml new file mode 100644 index 0000000..86c7a37 --- /dev/null +++ b/tests/test_gateddirective/solution-exercise-gated.xml @@ -0,0 +1,101 @@ +<document source="solution-exercise-gated.md"> + <section classes="tex2jax_ignore mathjax_ignore" ids="gated-solutions-to-exercise-gated-md" names="gated\ solutions\ to\ exercise-gated.md"> + <title> + Gated Solutions to exercise-gated.md + <paragraph> + A solution using the gated directive + <solution_node classes="solution" docname="solution-exercise-gated" hidden="False" ids="gated-exercise-solution-1" label="gated-exercise-solution-1" names="gated-exercise-solution-1" serial_number="0" target_label="gated-exercise-1" title="Solution to" type="solution"> + <solution_title> + Solution to + <section ids="solution-content"> + <paragraph> + This is a solution to Gated Exercise 1 + <CellNode cell_type="code" classes="cell"> + <CellInputNode classes="cell_input"> + <literal_block language="ipython3" xml:space="preserve"> + import numpy as np + import matplotlib.pyplot as plt + + # Fixing random state for reproducibility + np.random.seed(19680801) + + dt = 0.01 + t = np.arange(0, 30, dt) + nse1 = np.random.randn(len(t)) # white noise 1 + nse2 = np.random.randn(len(t)) # white noise 2 + + # Two signals with a coherent part at 10Hz and a random part + s1 = np.sin(2 * np.pi * 10 * t) + nse1 + s2 = np.sin(2 * np.pi * 10 * t) + nse2 + + fig, axs = plt.subplots(2, 1) + axs[0].plot(t, s1, t, s2) + axs[0].set_xlim(0, 2) + axs[0].set_xlabel('time') + axs[0].set_ylabel('s1 and s2') + axs[0].grid(True) + + cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) + axs[1].set_ylabel('coherence') + + fig.tight_layout() + plt.show() + <CellOutputNode classes="cell_output"> + <CellOutputBundleNode output_count="1"> + <paragraph> + With some follow up text to the solution + <paragraph> + and then a solution to + <pending_xref refdoc="solution-exercise-gated" refdomain="std" refexplicit="False" reftarget="gated-exercise-2" reftype="ref" refwarn="True"> + <inline classes="xref std std-ref"> + gated-exercise-2 + <paragraph> + A solution using the gated directive + <solution_node classes="solution" docname="solution-exercise-gated" hidden="False" ids="gated-exercise-solution-2" label="gated-exercise-solution-2" names="gated-exercise-solution-2" serial_number="1" target_label="gated-exercise-2" title="Solution to" type="solution"> + <solution_title> + Solution to + <section ids="solution-content"> + <paragraph> + This is a solution to Gated Exercise 2 + <CellNode cell_type="code" classes="cell"> + <CellInputNode classes="cell_input"> + <literal_block language="ipython3" xml:space="preserve"> + import numpy as np + import matplotlib.pyplot as plt + + # Fixing random state for reproducibility + np.random.seed(19680801) + + dt = 0.01 + t = np.arange(0, 30, dt) + nse1 = np.random.randn(len(t)) # white noise 1 + nse2 = np.random.randn(len(t)) # white noise 2 + + # Two signals with a coherent part at 10Hz and a random part + s1 = np.sin(2 * np.pi * 10 * t) + nse1 + s2 = np.sin(2 * np.pi * 10 * t) + nse2 + + fig, axs = plt.subplots(2, 1) + axs[0].plot(t, s1, t, s2) + axs[0].set_xlim(0, 2) + axs[0].set_xlabel('time') + axs[0].set_ylabel('s1 and s2') + axs[0].grid(True) + + cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) + axs[1].set_ylabel('coherence') + + fig.tight_layout() + plt.show() + <CellOutputNode classes="cell_output"> + <CellOutputBundleNode output_count="1"> + <paragraph> + With some follow up text to the solution + <section ids="references-to-solutions" names="references\ to\ solutions"> + <title> + References to Solutions + <paragraph> + This is a reference to + <pending_xref refdoc="solution-exercise-gated" refdomain="std" refexplicit="False" reftarget="gated-exercise-solution-1" reftype="ref" refwarn="True"> + <inline classes="xref std std-ref"> + gated-exercise-solution-1 diff --git a/tests/test_gateddirective/solution-exercise.xml b/tests/test_gateddirective/solution-exercise.xml new file mode 100644 index 0000000..f47bb88 --- /dev/null +++ b/tests/test_gateddirective/solution-exercise.xml @@ -0,0 +1,104 @@ +<document source="solution-exercise.md"> + <section classes="tex2jax_ignore mathjax_ignore" ids="gated-solutions-to-exercise-md" names="gated\ solutions\ to\ exercise.md"> + <title> + Gated Solutions to exercise.md + <paragraph> + A solution using the gated directive + <solution_node classes="solution" docname="solution-exercise" hidden="False" ids="solution-gated-1" label="solution-gated-1" names="solution-gated-1" serial_number="0" target_label="exercise-1" title="Solution to" type="solution"> + <solution_title> + Solution to + <section ids="solution-content"> + <paragraph> + This is a solution to Non-Gated Exercise 1 + <CellNode cell_type="code" classes="cell"> + <CellInputNode classes="cell_input"> + <literal_block language="ipython3" xml:space="preserve"> + import numpy as np + import matplotlib.pyplot as plt + + # Fixing random state for reproducibility + np.random.seed(19680801) + + dt = 0.01 + t = np.arange(0, 30, dt) + nse1 = np.random.randn(len(t)) # white noise 1 + nse2 = np.random.randn(len(t)) # white noise 2 + + # Two signals with a coherent part at 10Hz and a random part + s1 = np.sin(2 * np.pi * 10 * t) + nse1 + s2 = np.sin(2 * np.pi * 10 * t) + nse2 + + fig, axs = plt.subplots(2, 1) + axs[0].plot(t, s1, t, s2) + axs[0].set_xlim(0, 2) + axs[0].set_xlabel('time') + axs[0].set_ylabel('s1 and s2') + axs[0].grid(True) + + cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) + axs[1].set_ylabel('coherence') + + fig.tight_layout() + plt.show() + <CellOutputNode classes="cell_output"> + <CellOutputBundleNode output_count="1"> + <paragraph> + With some follow up text to the solution + <paragraph> + and a solution to + <pending_xref refdoc="solution-exercise" refdomain="std" refexplicit="False" reftarget="exercise-2" reftype="ref" refwarn="True"> + <inline classes="xref std std-ref"> + exercise-2 + <solution_node classes="solution" docname="solution-exercise" hidden="False" ids="solution-gated-2" label="solution-gated-2" names="solution-gated-2" serial_number="1" target_label="exercise-2" title="Solution to" type="solution"> + <solution_title> + Solution to + <section ids="solution-content"> + <paragraph> + This is a solution to Non-Gated Exercise 1 + <CellNode cell_type="code" classes="cell"> + <CellInputNode classes="cell_input"> + <literal_block language="ipython3" xml:space="preserve"> + import numpy as np + import matplotlib.pyplot as plt + + # Fixing random state for reproducibility + np.random.seed(19680801) + + dt = 0.01 + t = np.arange(0, 30, dt) + nse1 = np.random.randn(len(t)) # white noise 1 + nse2 = np.random.randn(len(t)) # white noise 2 + + # Two signals with a coherent part at 10Hz and a random part + s1 = np.sin(2 * np.pi * 10 * t) + nse1 + s2 = np.sin(2 * np.pi * 10 * t) + nse2 + + fig, axs = plt.subplots(2, 1) + axs[0].plot(t, s1, t, s2) + axs[0].set_xlim(0, 2) + axs[0].set_xlabel('time') + axs[0].set_ylabel('s1 and s2') + axs[0].grid(True) + + cxy, f = axs[1].cohere(s1, s2, 256, 1. / dt) + axs[1].set_ylabel('coherence') + + fig.tight_layout() + plt.show() + <CellOutputNode classes="cell_output"> + <CellOutputBundleNode output_count="1"> + <paragraph> + With some follow up text to the solution + <section ids="references" names="references"> + <title> + References + <paragraph> + This is a reference to + <pending_xref refdoc="solution-exercise" refdomain="std" refexplicit="False" reftarget="solution-gated-1" reftype="ref" refwarn="True"> + <inline classes="xref std std-ref"> + solution-gated-1 + <paragraph> + This is a reference to + <pending_xref refdoc="solution-exercise" refdomain="std" refexplicit="False" reftarget="solution-gated-2" reftype="ref" refwarn="True"> + <inline classes="xref std std-ref"> + solution-gated-2