Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 17 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,23 @@ jobs:
- name: Build Docs
run: |
pip install tox-uv
tox -e docs-py310 -- -r
tox -e docs-py310

linkcheck:
name: "Link Check"
runs-on: ubuntu-latest
needs: [lint]
continue-on-error: true
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7
with:
persist-credentials: false
- uses: actions/setup-python@a309ff8b426b58ec0e2a45f0f869d46889d02405 # v6
with: {python-version: "3.10"}
- name: Check external links
run: |
pip install tox-uv
tox -e linkcheck

typecheck:
needs: [lint]
Expand Down
3 changes: 2 additions & 1 deletion baybe/recommenders/meta/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,8 @@ def get_non_meta_recommender(
) -> RecommenderProtocol:
"""Follow the meta recommender chain to the selected non-meta recommender.

Recursively calls :meth:`MetaRecommender.select_recommender` until a
Recursively calls
:meth:`~baybe.recommenders.meta.base.MetaRecommender.select_recommender` until a
non-meta recommender is encountered, which is then returned.
Effectively, this extracts the recommender responsible for generating
the recommendations for the specified context.
Expand Down
2 changes: 1 addition & 1 deletion baybe/surrogates/gaussian_process/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -306,7 +306,7 @@ def posterior_mean_function(
* **Eagerly:** By calling the method and passing the returned module to a GP.
* **Lazily:** By passing the bound method itself, without eagerly calling it.
This works because the method signature complies with
:class:`~.components.mean.MeanFactoryProtocol`, i.e., the new GP will use it
:obj:`~.components.mean.MeanFactoryProtocol`, i.e., the new GP will use it
as a factory and call it automatically at fit time.

If the mean-providing GP has not been fitted at call time, its prior mean module
Expand Down
17 changes: 9 additions & 8 deletions baybe/transformations/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,10 +43,11 @@ def get_codomain(self, interval: Interval | None = None, /) -> Interval:
In accordance with the mathematical definition of a function's `codomain
<https://en.wikipedia.org/wiki/Codomain>`_, we define the codomain of a given
:class:`~baybe.utils.interval.Interval` under a certain (assumed continuous)
:class:`~Transformation` to be an :class:`~baybe.utils.interval.Interval`
guaranteed to contain all possible outcomes when the :class:`~Transformation` is
applied to all points in the input :class:`~baybe.utils.interval.Interval`. In
cases where the image cannot exactly be computed, it is often still possible to
:class:`~baybe.transformations.base.Transformation` to be an
:class:`~baybe.utils.interval.Interval` guaranteed to contain all possible
outcomes when the :class:`~baybe.transformations.base.Transformation` is applied
to all points in the input :class:`~baybe.utils.interval.Interval`. In cases
where the image cannot exactly be computed, it is often still possible to
compute a codomain. The codomain always contains the image, but might be larger.
"""

Expand All @@ -56,10 +57,10 @@ def get_image(self, interval: Interval | None = None, /) -> Interval:
In accordance with the mathematical definition of a function's `image
<https://en.wikipedia.org/wiki/Image_(mathematics)>`_, we define the image of a
given :class:`~baybe.utils.interval.Interval` under a certain (assumed
continuous) :class:`~Transformation` to be the smallest
:class:`~baybe.utils.interval.Interval` containing all possible outcomes when
the :class:`~Transformation` is applied to all points in the input
:class:`~baybe.utils.interval.Interval`.
continuous) :class:`~baybe.transformations.base.Transformation` to be the
smallest :class:`~baybe.utils.interval.Interval` containing all possible
outcomes when the :class:`~baybe.transformations.base.Transformation` is applied
to all points in the input :class:`~baybe.utils.interval.Interval`.
"""
# By default, it is assumed that the exact image of an interval cannot be
# computed but only the codomain is available (see :meth:`get_codomain`).
Expand Down
17 changes: 17 additions & 0 deletions docs/api_reference.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
---
orphan: true
---

<!-- This page exists solely to trigger the recursive autosummary stub generation for
the API reference. The generated `_autosummary/baybe` tree is linked from the "API
Reference" entry of the main toctree in `index.md`. Keeping the directive off the
landing page prevents the module summary table from being rendered there. -->

```{eval-rst}
.. autosummary::
:toctree: _autosummary
:template: custom-module-template.rst
:recursive:

baybe
```
2 changes: 1 addition & 1 deletion docs/components/transformations.md
Original file line number Diff line number Diff line change
Expand Up @@ -399,7 +399,7 @@ t = CustomTransformation(torch.sin)
```

````{admonition} Automatic Wrapping
:note:
:class: note

When embedding custom transformations into another context, wrapping the `torch`
callable into a {class}`~baybe.transformations.basic.CustomTransformation` happens
Expand Down
58 changes: 48 additions & 10 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -139,7 +139,6 @@
templates_path = ["templates"]
# Tell sphinx which files should be excluded
exclude_patterns = ["sdk", "AGENTS.md", "CLAUDE.md", "**/AGENTS.md", "**/CLAUDE.md"]
autodoc_exclude_modules = ["baybe.utils.clustering_algorithms.third_party.kmedoids"]

# Enable markdown
# Note that we do not need additional configuration here.
Expand All @@ -151,27 +150,59 @@
# Here, we define regex expressions for errors produced by nitpick that we want to
# ignore.
nitpick_ignore_regex = [
# Ignore everything that does not include baybe
(r"py:.*", r"^(?!.*baybe).*"),
# Ignore errors that are from inherited classes we cannot control
##### External package references #####
# Qualified references to external packages whose internal module paths cannot be
# resolved via intersphinx (e.g. pandas.core.frame.DataFrame vs pandas.DataFrame).
(
r"py:.*",
r"(pandas|numpy|torch|botorch|gpytorch|scipy|sklearn|pathlib|polars|attr|joblib|matplotlib|skfp|rdkit|shap|xyzpy|typing)[\._].*",
), # noqa: E501
##### Inherited torch.nn.Module docstring references #####
# Unqualified names from inherited external docstrings (torch, botorch, sklearn)
# that cannot be resolved outside their original documentation context.
(r"py:class", r"^(Tensor|Module|Parameter|Dropout|BatchNorm)$"),
(r"py:class", r"^(Posterior|MetadataRequest|Ignored)$"),
(r"py:attr", r"^(persistent|grad_input|grad_output|requires_grad)$"),
(r"py:attr", r"^(device|dtype|dst_type|non_blocking)$"),
(r"py:func", r"^(register_module_forward_hook|register_module_forward_pre_hook)$"),
(r"py:func", r"^(register_module_full_backward_hook)$"),
(r"py:func", r"^(register_module_full_backward_pre_hook|load_state_dict)$"),
(r"py:meth", r"^nn\.Module\.load_state_dict$"),
##### Inherited sklearn/scipy docstring artifacts #####
# sklearn docstrings use informal type descriptions that Sphinx parses as refs.
(r"py:class", r"^(optional|shape|shape=|n_samples|n_features|n_query)$"),
(r"py:class", r"^(n_features_new|n_outputs|n_indexed|n_clusters)$"),
(r"py:class", r"^(array-like|ndarray|ndarray array|string)$"),
(r"py:class", r"^(estimator instance|sparse matrix\})$"),
(r"py:class", r"^(\{array-like|default=.*|\{\"default\")$"),
(r"py:class", r"^(dtype=np\.int64|if metric == 'precomputed')$"),
##### Type aliases in TYPE_CHECKING blocks #####
# These exist only at type-checking time and cannot be resolved by Sphinx.
(r"py:class", r"^(GPComponent|TensorCallable|ConvertibleToFloat)$"),
(r"py:class", r"^(GPyTorchKernel|GPyTorchLikelihood|GPyTorchMean|GPyTorchModel)$"),
(r"py:class", r"^(pd\.DataFrame|pl\.Expr)$"),
(r"py:class", r"^(TypeAliasForwardRef|P)$"),
(r"py:class", r"^\"(pandas|polars)\"\}?$"),
##### BayBE-specific suppressions #####
# Inherited classes we cannot control
(r"py:.*", r".*DTypeFloatONNX.*"),
# Ignore the functions that we manually delete from in child classes
# Serialization functions manually deleted from child classes
(r"py:.*", r".*from_dict.*"),
(r"py:.*", r".*from_json.*"),
(r"py:.*", r".*to_dict.*"),
(r"py:.*", r".*to_json.*"),
(r"py:.*", r".*_T.*"),
# Ignore files for which no __init__ is available at all
# Classes for which no __init__ is available at all
(r"py:.*", "baybe.constraints.conditions.Condition.__init__"),
(r"py:.*", "baybe.serialization.mixin.SerialMixin.__init__"),
(r"DeprecationWarning:", ""),
# Ignore the generics/aliases
# Generics/aliases
(r"py:class", "baybe.utils.basic._C"),
(r"py:class", "baybe.utils.basic._T"),
(r"py:class", "baybe.utils.basic._U"),
(r"py:class", "baybe.surrogates.composite._SurrogateGetter"),
(r"ref:obj", "baybe.surrogates.base.ModelContext"),
# Ignore custom class properties
# Custom class properties
(r"py:obj", "baybe.settings._AdoptedRandomSeed.*"),
(r"py:obj", "baybe.acquisition.acqfs.*.supports_batching"),
(r"py:obj", "baybe.acquisition.acqfs.*.supports_pending_experiments"),
Expand Down Expand Up @@ -208,8 +239,15 @@
]


# Ignore the warnings that are given by autosectionlabel
suppress_warnings = ["autosectionlabel.*"]
# Ignore certain warning categories
suppress_warnings = [
"autosectionlabel.*",
# Forward reference and guarded import warnings from sphinx-autodoc-typehints.
# These are unavoidable since heavy deps (torch, botorch, gpytorch) are lazy-loaded
# and only available in TYPE_CHECKING blocks at runtime.
"sphinx_autodoc_typehints.forward_reference",
"sphinx_autodoc_typehints.guarded_import",
]

# -- Options for HTML output -------------------------------------------------
# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output
Expand Down
10 changes: 0 additions & 10 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,6 @@ FAQ <faq>
:relative-docs: docs/
```

```{eval-rst}
.. autosummary::
:toctree: _autosummary
:template: custom-module-template.rst
:recursive:
:hidden:

baybe
```

```{toctree}
:maxdepth: 2
:titlesonly:
Expand Down
29 changes: 5 additions & 24 deletions docs/scripts/build_documentation.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@
from subprocess import check_call, run

from build_examples import build_examples
from check_links import check_links
from utils import adjust_pictures

parser = argparse.ArgumentParser()
Expand All @@ -16,16 +15,10 @@
help="Re-run the examples.",
action="store_true",
)
parser.add_argument(
"-l",
"--no_linkcheck",
help="Do not check the links.",
action="store_true",
)
parser.add_argument(
"-r",
"--full-rebuild",
help="Perform a full rebuild, independent of `-e` and `-l` flags.",
help="Perform a full rebuild, independent of `-e` flag.",
action="store_true",
)
parser.add_argument(
Expand All @@ -44,24 +37,22 @@
# Parse input arguments
args = parser.parse_args()
RUN_EXAMPLES = args.run_examples
LINKCHECK = not args.no_linkcheck
FULL_REBUILD = args.full_rebuild
INCLUDE_WARNINGS = args.include_warnings
FORCE = args.force


def build_documentation(
run_examples: bool = False,
verify_links: bool = False,
full_rebuild: bool = False,
force: bool = False,
) -> None:
"""Build the documentation.

A full build of the documentation consists of converting the examples into jupyter
notebooks, executing them, transforming them into markdown files, as well as
checking all links and performing the actual ``sphinx-build``. Such a full build can
be triggered using the ``full_rebuild`` flag.
notebooks, executing them, transforming them into markdown files, and performing
the actual ``sphinx-build``. Such a full build can be triggered using the
``full_rebuild`` flag.
If this flag is not set, this function tries to re-use as much of potentially
existing structures like already built examples as possible. This behavior can be
changed by using the other flags.
Expand All @@ -70,18 +61,14 @@ def build_documentation(
run_examples: Fully recalculate the examples. If this is ``False`` and no
folder containing an already built set of examples is found, dummy files
replicating the structure of the examples are created.
verify_links: Check both internal and external links.
full_rebuild: Perform a full rebuild of the documentation, including a
recalculation of the examples and checking the links. Note that this option
ignores the choices for ``run_examples`` and ``check_links`` if set to
``True.
recalculation of the examples.
force: Force-build the steps, ignoring any errors or warnings.
"""
examples_directory = pathlib.Path("docs/examples")
examples_exist = examples_directory.is_dir()

rerun_examples = run_examples or full_rebuild
perform_linkcheck = verify_links or full_rebuild

if rerun_examples:
build_examples(
Expand All @@ -98,9 +85,6 @@ def build_documentation(
remove_dir=examples_exist,
)

if perform_linkcheck:
check_links()

# Directory where the documentation is build.
build_dir = pathlib.Path("docs/build")

Expand Down Expand Up @@ -128,11 +112,8 @@ def build_documentation(
if not INCLUDE_WARNINGS:
os.environ["PYTHONWARNINGS"] = "ignore"

print(f"{LINKCHECK=}")

build_documentation(
run_examples=RUN_EXAMPLES,
verify_links=LINKCHECK,
full_rebuild=FULL_REBUILD,
force=FORCE,
)
Expand Down
8 changes: 6 additions & 2 deletions docs/scripts/check_links.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
"""Utility for checking the links of the documentation."""
"""Utility for checking the external links of the documentation."""

from subprocess import check_call


def check_links() -> None:
"""Check whether the links of the documentation are valid."""
"""Check whether the external links of the documentation are valid."""
link_call = [
"sphinx-build",
"-b",
Expand All @@ -14,3 +14,7 @@ def check_links() -> None:
]

check_call(link_call)


if __name__ == "__main__":
check_links()
2 changes: 1 addition & 1 deletion docs/templates/custom-module-template.rst
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@
:template: custom-module-template.rst
:recursive:
{% for item in modules %}
{% if not item in ("baybe.objectives.deprecation", "baybe.recommenders.pure.bayesian.sequential_greedy") %}
{% if not item in ("kmedoids",) %}
{{ item }}
{%- endif %}
{%- endfor %}
Expand Down
13 changes: 11 additions & 2 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -102,10 +102,19 @@ commands =
uv run --locked --extra docs docs/scripts/build_documentation.py {posargs}

[testenv:docs-quickbuild]
description = Force-build documentation, ignoring links and examples
description = Force-build documentation, ignoring examples
skip_install = True
setenv =
SMOKE_TEST = true
commands =
python --version
uv run --locked --extra docs docs/scripts/build_documentation.py -f -l
uv run --locked --extra docs docs/scripts/build_documentation.py -f

[testenv:linkcheck]
description = Check external links in the documentation
skip_install = True
setenv =
SMOKE_TEST = true
commands =
python --version
uv run --locked --extra docs docs/scripts/check_links.py
Loading