Skip to content

Commit

Permalink
WIP: added sphinx gallery
Browse files Browse the repository at this point in the history
  • Loading branch information
mpvanderschelling committed May 15, 2024
1 parent ef25ae6 commit 7b4d1b5
Show file tree
Hide file tree
Showing 33 changed files with 787 additions and 23 deletions.
1 change: 1 addition & 0 deletions .flake8
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ extend-exclude =
tests
.git
studies
examples

# <<< UPDATE ACCORDING WITH YOUR PYTHON PROJECT

Expand Down
6 changes: 5 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,8 @@ notebooks/tutorials/Data/
# Exclude obj files in tests folder
!tests/*/*.obj

*.lock
*.lock

# Exclude sphinx gallery files
docs/source/auto_examples/
docs/source/gen_modules/
2 changes: 1 addition & 1 deletion docs/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ help:

clean:
-rm -rf $(BUILDDIR)/*

-rm -rf $(SOURCEDIR)/auto_examples/*
html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(SOURCEDIR) $(BUILDDIR)/html
@echo
Expand Down
3 changes: 2 additions & 1 deletion docs/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@ sphinx
sphinx_rtd_theme
sphinxcontrib-bibtex
sphinx_autodoc_typehints
sphinx-tabs==3.4.4
sphinx-tabs==3.4.4
sphinx-gallery
11 changes: 10 additions & 1 deletion docs/source/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,16 @@
'sphinx.ext.intersphinx',
'sphinx.ext.viewcode',
'sphinx_autodoc_typehints',
'sphinx_tabs.tabs']
'sphinx_tabs.tabs',
'sphinx_gallery.gen_gallery',]

sphinx_gallery_conf = {
'examples_dirs': ['../../examples'], # path to your example scripts
'gallery_dirs': ['auto_examples'],
'reference_url': {'sphinx_gallery': None, },
'backreferences_dir': 'gen_modules/backreferences',
'doc_module': ('f3dasm',),
}

# Source: https://www.sphinx-doc.org/en/master/usage/configuration.html#confval-source_suffix
source_suffix = {'.rst': 'restructuredtext', }
Expand Down
2 changes: 2 additions & 0 deletions docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ f3dasm
:glob:

rst_doc_files/classes/design/experimentdata
auto_examples/plot_experimentdata_storing
rst_doc_files/classes/design/experimentsample

.. toctree::
Expand Down Expand Up @@ -78,5 +79,6 @@ f3dasm

rst_doc_files/reference/index.rst
Code <_autosummary/f3dasm>
auto_examples/index.rst

.. include:: readme.rst
28 changes: 24 additions & 4 deletions docs/source/rst_doc_files/classes/design/domain.rst
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,10 @@ Now we can add some parameters!

.. _parameters:

Parameters
----------
Input parameters
----------------

Parameters are singular features of the input search space. They are used to define the search space of the design.
Input parameters are singular features of the input search space. They are used to define the search space of the design.

.. image:: ../../../img/f3dasm-parameter.png
:width: 50%
Expand Down Expand Up @@ -99,6 +99,21 @@ Constant parameters
.. _domain-from-yaml:

Output parameters
-----------------

Output parameters are the results of evaluating the input design with a data generation model.
Output parameters can hold any type of data, e.g. a scalar value, a vector, a matrix, etc.
Normally, you would not need to define output parameters, as they are created automatically when you store a variable to the :class:`~f3dasm.ExperimentData` object.

.. code-block:: python
domain.add_output(name='y', to_disk=False)
The :code:`to_disk` argument can be set to :code:`True` to store the output parameter on disk. A reference to the file is stored in the :class:`~f3dasm.ExperimentData` object.
This is useful when the output data is very large, or when the output data is an array-like object.
More information on storing output can be found in :ref:`this section <storing-output-experiment-sample>`

Domain from a `hydra <https://hydra.cc/>`_ configuration file
-------------------------------------------------------------

Expand Down Expand Up @@ -136,7 +151,7 @@ The domain can now be created by calling the :func:`~f3dasm.design.Domain.from_y
def my_app(cfg):
domain = Domain.from_yaml(cfg.domain)
Helper function for single-objective, n-dimensional continuous Domains
Helper function for single-objective, n-dimensional continuous domains
----------------------------------------------------------------------

We can make easily make a :math:`n`-dimensional continous domain with the helper function :func:`~f3dasm.design.make_nd_continuous_domain`.
Expand All @@ -148,3 +163,8 @@ We have to specify the boundaries (``bounds``) for each of the dimensions with a
import numpy as np
bounds = np.array([[-1.0, 1.0], [-1.0, 1.0]])
domain = make_nd_continuous_domain(bounds=bounds, dimensionality=2)
.. minigallery:: f3dasm.design.Domain
:add-heading: Examples using the `Domain` object
:heading-level: -
6 changes: 5 additions & 1 deletion docs/source/rst_doc_files/classes/design/experimentdata.rst
Original file line number Diff line number Diff line change
Expand Up @@ -179,7 +179,7 @@ If the directory does not exist, it will be created.
>>> project_dir = "folder/to/my_project_directory"
>>> data = ExperimentData(project_dir=project_dir)
You can also set the project directoy manually after creation with the :meth:`~f3dasm.ExperimentData.set_project_dir` method"
You can also set the project directory manually after creation with the :meth:`~f3dasm.ExperimentData.set_project_dir` method"

.. code-block:: python
Expand Down Expand Up @@ -394,3 +394,7 @@ Alternatively, you can convert the input- and outputdata of your data-driven pro
* :class:`~numpy.ndarray` (:meth:`~f3dasm.ExperimentData.to_numpy`); creates a tuple of two :class:`~numpy.ndarray` objects containing the input- and outputdata.
* :class:`~xarray.Dataset` (:meth:`~f3dasm.ExperimentData.to_xarray`); creates a :class:`~xarray.Dataset` object containing the input- and outputdata.
* :class:`~pd.DataFrame` (:meth:`~f3dasm.ExperimentData.to_pandas`); creates a tuple of two :class:`~pd.DataFrame` object containing the input- and outputdata.

.. minigallery:: f3dasm.ExperimentData
:add-heading: Examples using the `ExperimentData` object
:heading-level: -
2 changes: 2 additions & 0 deletions docs/source/rst_doc_files/classes/design/experimentsample.rst
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,8 @@ The :class:`~f3dasm.ExperimentData` object can be manually iterated over to get
ExperimentSample(1 : {'x0': 0.7203461491873061, 'x1': 0.7320604457665572, 'x2': 0.2524387342272223} - {})
ExperimentSample(2 : {'x0': 0.35449352388104904, 'x1': 0.11413412225748525, 'x2': 0.1467895592274866} - {})
.. _storing-output-experiment-sample:

Storing output parameters to the experiment sample
--------------------------------------------------

Expand Down
51 changes: 37 additions & 14 deletions docs/source/rst_doc_files/classes/sampling/sampling.rst
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,58 @@
Sampling
========

Samplers take the :class:`~f3dasm.design.Domain` object and return input data based on the sampling strategy.
In the context of the data-driven process, samplers play a crucial role in generating input data for experiments or analyses.
A sampler takes a :class:`~f3dasm.design.Domain` object, and applies a specific strategy to produce samples.
These samples serve as input for further analysis, experimentation, or modeling.

This section describes how you can implement your sampling strategy or use the built-in samplers in a data-driven process.

.. _integrating-samplers:

Implement your sampling strategy
--------------------------------

To integrate your sampling strategy in in the data-driven process, you should create a function that takes the following arguments:
When integrating your sampling strategy into the data-driven process, you have to create a function that will take several arguments:

* :code:`domain`: A :class:`~f3dasm.design.Domain` object that represents the design-of-experiments
* :code:`n_samples`: The number of samples you wish to generate. It's not always necessary to define this upfront, as some sampling methods might inherently determine the number of samples based on certain criteria or properties of the domain.
* :code:`seed`: A seed for the random number generator to replicate the sampling process. This enables you to control the randomness of the sampling process [1]_. By setting a seed, you ensure reproducibility, meaning that if you run the sampling function with the same seed multiple times, you'll get the same set of samples.

* A :class:`~f3dasm.design.Domain` object
* The number of samples to create
* A random seed (optional)
.. [1] If no seed is provided, the function should use a random seed.
The function should return the samples (``input_data``) in one of the following formats:

* A :class:`~pandas.DataFrame` object
* A :class:`~numpy.ndarray` object

.. code-block:: python
def your_sampling_method(domain: Domain, n_samples: int, seed: Optional[int]):
# Your sampling logic here
...
return your_samples
.. _implemented samplers:
An example: implementing a sobol sequence sampler
-------------------------------------------------

Use the sampler in the data-driven process
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
For example, the following code defines a sampler based on a `Sobol sequence <https://salib.readthedocs.io/en/latest/api/SALib.sample.html?highlight=sobol%20sequence#SALib.sample.sobol_sequence.sample>`_:

.. code-block:: python
from f3dasm.design import Domain
from SALib.sample import sobol_sequence
def sample_sobol_sequence(domain: Domain, n_samples: int, **kwargs):
samples = sobol_sequence.sample(n_samples, len(domain))
# stretch samples
for dim, param in enumerate(domain.space.values()):
samples[:, dim] = (
samples[:, dim] * (
param.upper_bound - param.lower_bound
) + param.lower_bound
)
return samples
To use the sampler in the data-driven process, you should pass the function to the :class:`~f3dasm.ExperimentData` object as follows:

Expand All @@ -41,17 +69,12 @@ To use the sampler in the data-driven process, you should pass the function to t
# Generate samples
experiment_data.sample(sampler=your_sampling_method, n_samples=10, seed=42)
.. note::

This method will throw an error if you do not have any prior ``input_data`` in the :class:`~f3dasm.ExperimentData`
object before sampling **and** you do not provide a :class:`~f3dasm.design.Domain` object in the initializer.
.. _implemented samplers:

Implemented samplers
--------------------

The following built-in implementations of samplers can be used in the data-driven process.
To use these samplers

======================== ====================================================================== ===========================================================================================================
Name Method Reference
Expand Down
52 changes: 52 additions & 0 deletions docs/source/sg_execution_times.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@

:orphan:

.. _sphx_glr_sg_execution_times:


Computation times
=================
**00:01.116** total execution time for 6 files **from all galleries**:

.. container::

.. raw:: html

<style scoped>
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/5.3.0/css/bootstrap.min.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/1.13.6/css/dataTables.bootstrap5.min.css" rel="stylesheet" />
</style>
<script src="https://code.jquery.com/jquery-3.7.0.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/1.13.6/js/dataTables.bootstrap5.min.js"></script>
<script type="text/javascript" class="init">
$(document).ready( function () {
$('table.sg-datatable').DataTable({order: [[1, 'desc']]});
} );
</script>

.. list-table::
:header-rows: 1
:class: table table-striped sg-datatable

* - Example
- Time
- Mem (MB)
* - :ref:`sphx_glr_auto_examples_hydra_plot_hydra_usage.py` (``../../examples/hydra/plot_hydra_usage.py``)
- 00:00.873
- 0.0
* - :ref:`sphx_glr_auto_examples_domain_plot_builtin_sampler.py` (``../../examples/domain/plot_builtin_sampler.py``)
- 00:00.171
- 0.0
* - :ref:`sphx_glr_auto_examples_experimentdata_plot_experimentdata.py` (``../../examples/experimentdata/plot_experimentdata.py``)
- 00:00.032
- 0.0
* - :ref:`sphx_glr_auto_examples_domain_plot_own_sampler.py` (``../../examples/domain/plot_own_sampler.py``)
- 00:00.021
- 0.0
* - :ref:`sphx_glr_auto_examples_experimentdata_plot_experimentdata_storing.py` (``../../examples/experimentdata/plot_experimentdata_storing.py``)
- 00:00.015
- 0.0
* - :ref:`sphx_glr_auto_examples_domain_plot_domain_creation.py` (``../../examples/domain/plot_domain_creation.py``)
- 00:00.004
- 0.0
4 changes: 4 additions & 0 deletions examples/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
This is my gallery
==================

Below is a gallery of examples
4 changes: 4 additions & 0 deletions examples/domain/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Domain
------

Below is a gallery of examples
51 changes: 51 additions & 0 deletions examples/domain/plot_builtin_sampler.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
"""
Use the built-in sampling strategies
====================================
In this example, we will use the built-in sampling strategies provided by :mod:`f3dasm` to generate samples for a data-driven experiment.
"""

from matplotlib import pyplot as plt

from f3dasm import ExperimentData
from f3dasm.design import make_nd_continuous_domain

###############################################################################
# We create 2D continuous input domain with the :func:`~f3dasm.design.make_nd_continuous_domain` helper function:

domain = make_nd_continuous_domain(bounds=[[0., 1.], [0., 1.]])
print(domain)

###############################################################################
# You can create an :class:`~f3dasm.ExperimentData` object with the :meth:`~f3dasm.ExperimentData.from_sampling` constructor directly:

data_random = ExperimentData.from_sampling(
domain=domain, n_samples=10, sampler='random', seed=42)

fig, ax = plt.subplots(figsize=(4, 4))

print(data_random)

df_random, _ = data_random.to_pandas()
ax.scatter(df_random.iloc[:, 0], df_random.iloc[:, 1])
ax.set_xlabel(domain.names[0])
ax.set_ylabel(domain.names[1])

###############################################################################
# :mod:`f3dasm` provides several built-in samplers.
# The example below shows how to use the Latin Hypercube Sampling (LHS) sampler:

data_lhs = ExperimentData.from_sampling(
domain=domain, n_samples=10, sampler='latin', seed=42)

fig, ax = plt.subplots(figsize=(4, 4))

print(data_lhs)

df_lhs, _ = data_lhs.to_pandas()
ax.scatter(df_lhs.iloc[:, 0], df_lhs.iloc[:, 1])
ax.set_xlabel(domain.names[0])
ax.set_ylabel(domain.names[1])

###############################################################################
# More information all the available samplers can be found in :ref:`here <implemented samplers>`.
Loading

0 comments on commit 7b4d1b5

Please sign in to comment.