Skip to content

Commit

Permalink
updated documentation
Browse files Browse the repository at this point in the history
  • Loading branch information
mpvanderschelling committed May 15, 2024
1 parent 7b4d1b5 commit 0e3aa97
Show file tree
Hide file tree
Showing 12 changed files with 325 additions and 27 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,8 @@ Benchmark functions can substitute the expensive simulation in the
experiment_data.evaluate('Ackley', kwargs={'noise': 0.1, 'scale_bounds': [[0., 1.], [-1., 1.]], 'offset': True, 'seed': 42})
.. _implemented-benchmark-functions:

Implemented benchmark functions
-------------------------------

Expand Down
26 changes: 16 additions & 10 deletions docs/source/sg_execution_times.rst
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

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

.. container::

Expand All @@ -32,21 +32,27 @@ Computation times
* - Example
- Time
- Mem (MB)
* - :ref:`sphx_glr_auto_examples_hydra_plot_hydra_usage.py` (``../../examples/hydra/plot_hydra_usage.py``)
- 00:00.873
* - :ref:`sphx_glr_auto_examples_datageneration_plot_own_datagenerator.py` (``../../examples/datageneration/plot_own_datagenerator.py``)
- 00:00.467
- 0.0
* - :ref:`sphx_glr_auto_examples_datageneration_plot_builtin_benchmarkfunctions.py` (``../../examples/datageneration/plot_builtin_benchmarkfunctions.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_auto_examples_domain_plot_builtin_sampler.py` (``../../examples/domain/plot_builtin_sampler.py``)
- 00:00.171
- 00:00.000
- 0.0
* - :ref:`sphx_glr_auto_examples_experimentdata_plot_experimentdata.py` (``../../examples/experimentdata/plot_experimentdata.py``)
- 00:00.032
* - :ref:`sphx_glr_auto_examples_domain_plot_domain_creation.py` (``../../examples/domain/plot_domain_creation.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_auto_examples_domain_plot_own_sampler.py` (``../../examples/domain/plot_own_sampler.py``)
- 00:00.021
- 00:00.000
- 0.0
* - :ref:`sphx_glr_auto_examples_experimentdata_plot_experimentdata.py` (``../../examples/experimentdata/plot_experimentdata.py``)
- 00:00.000
- 0.0
* - :ref:`sphx_glr_auto_examples_experimentdata_plot_experimentdata_storing.py` (``../../examples/experimentdata/plot_experimentdata_storing.py``)
- 00:00.015
- 00:00.000
- 0.0
* - :ref:`sphx_glr_auto_examples_domain_plot_domain_creation.py` (``../../examples/domain/plot_domain_creation.py``)
- 00:00.004
* - :ref:`sphx_glr_auto_examples_hydra_plot_hydra_usage.py` (``../../examples/hydra/plot_hydra_usage.py``)
- 00:00.000
- 0.0
6 changes: 3 additions & 3 deletions examples/README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
This is my gallery
==================
Examples
========

Below is a gallery of examples
Below is a gallery of examples that use various part of the :mod:`f3dasm` package
4 changes: 4 additions & 0 deletions examples/datageneration/README.rst
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
Data generation
---------------

Examples that use the :mod:`f3dasm.datageneration` module to generate output for the data generation process.
62 changes: 62 additions & 0 deletions examples/datageneration/plot_builtin_benchmarkfunctions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
"""
Use the built-in benchmark functions
====================================
In this example, we will use the built-in benchmark functions provided by :mod:`f3dasm.datageneration.functions` to generate output for a data-driven experiment.
"""

import matplotlib.pyplot as plt

from f3dasm import ExperimentData
from f3dasm.design import make_nd_continuous_domain

###############################################################################
# :mod:`f3dasm` ships with a set of benchmark functions that can be used to test the performance of
# optimization algorithms or to mock some expensive simulation in order to test the data-driven process.
# These benchmark functions are taken and modified from the `Python Benchmark Test Optimization Function Single Objective <https://github.com/AxelThevenot/Python_Benchmark_Test_Optimization_Function_Single_Objective>`_ github repository.
#
# Let's start by creating a continuous domain
# with 2 input variables, each ranging from -1.0 to 1.0

domain = make_nd_continuous_domain([[-1., 1.], [-1., 1.]])

###############################################################################
# We generate the input data by sampling the domain equally spaced with the grid sampler and create the :class:`~f3dasm.ExperimentData` object:

experiment_data = ExperimentData.from_sampling(
'grid', domain=domain, stepsize_continuous_parameters=0.1)

print(experiment_data)

###############################################################################
# Evaluating a 2D version of the Ackley function is as simple as
# calling the :meth:`~f3dasm.ExperimentData.evaluate` method with the function name as the ``data_generator`` argument.
#
# In addition, you can provide a dictionary (``kwargs``) with the followinging keywords to the :class:`~f3dasm.design.ExperimentData.evaluate` method:
#
# * ``scale_bounds``: A 2D list of floats that define the scaling lower and upper boundaries for each dimension. The normal benchmark function box-constraints will be scaled to these boundaries.
# * ``noise``: A float that defines the standard deviation of the Gaussian noise that is added to the objective value.
# * ``offset``: A boolean value. If ``True``, the benchmark function will be offset by a constant vector that will be randomly generated [1]_.
# * ``seed``: Seed for the random number generator for the ``noise`` and ``offset`` calculations.
#
# .. [1] As benchmark functions usually have their minimum at the origin, the offset is used to test the robustness of the optimization algorithm.

experiment_data.evaluate(data_generator='Ackley', kwargs={
'scale_bounds': domain.get_bounds(), 'offset': False})

###############################################################################
# The function values are stored in the ``y`` variable of the output data:

print(experiment_data)

###############################################################################

arr_in, arr_out = experiment_data.to_numpy()
fig, ax = plt.subplots(subplot_kw={'projection': '3d'})
ax.scatter(arr_in[:, 0], arr_in[:, 1], arr_out.ravel())
_ = ax.set_xlabel('$x_0$')
_ = ax.set_ylabel('$x_1$')
_ = ax.set_zlabel('$f(x)$')

###############################################################################
# A complete list of all the implemented benchmark functions can be found :ref:`here <implemented-benchmark-functions>`
173 changes: 173 additions & 0 deletions examples/datageneration/plot_own_datagenerator.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,173 @@
"""
Implement your own datagenerator: car stopping distance problem
===============================================================
In this example, we will implement a custom data generator that generates output for a data-driven experiment.
We will use the 'car stopping distance' problem as an example.
"""

import matplotlib.pyplot as plt
import numpy as np
from scipy.stats import norm

from f3dasm import ExperimentData
from f3dasm.datageneration import DataGenerator
from f3dasm.design import Domain

###############################################################################
#
# Car stopping distance :math:`y` as a function of its velocity :math:`x` before it starts braking:
#
# .. math::
#
# y = z x + \frac{1}{2 \mu g} x^2 = z x + 0.1 x^2
#
#
# - :math:`z` is the driver's reaction time (in seconds)
# - :math:`\mu` is the road/tires coefficient of friction (we assume :math:`\mu=0.5`)
# - :math:`g` is the acceleration of gravity (assume :math:`g=10 m/s^2`).
#
# .. math::
#
# y = d_r + d_{b}
#
# where :math:`d_r` is the reaction distance, and :math:`d_b` is the braking distance.
#
# Reaction distance :math:`d_r`
#
# .. math::
#
# d_r = z x
#
# with :math:`z` being the driver's reaction time, and :math:`x` being the velocity of the car at the start of braking.
#
# Kinetic energy of moving car:
#
# .. math::
#
# E = \frac{1}{2}m x^2
#
# where :math:`m` is the car mass.
#
# Work done by braking:
#
# .. math::
#
# W = \mu m g d_b
#
#
# where :math:`\mu` is the coefficient of friction between the road and the tire, :math:`g` is the acceleration of gravity, and :math:`d_b` is the car braking distance.
#
# The braking distance follows from :math:`E=W`:
#
# .. math::
#
# d_b = \frac{1}{2\mu g}x^2
#
# Therefore, if we add the reacting distance :math:`d_r` to the braking distance :math:`d_b` we get the stopping distance :math:`y`:
#
# .. math::
#
# y = d_r + d_b = z x + \frac{1}{2\mu g} x^2
#
#
# Every driver has its own reaction time :math:`z`
# Assume the distribution associated to :math:`z` is Gaussian with mean :math:`\mu_z=1.5` seconds and variance :math:`\sigma_z^2=0.5^2`` seconds\ :sup:`2`:
#
# .. math::
#
# z \sim \mathcal{N}(\mu_z=1.5,\sigma_z^2=0.5^2)
#
#
# We create a function that generates the stopping distance :math:`y` given the velocity :math:`x` and the reaction time :math:`z`:


def y(x):
z = norm.rvs(1.5, 0.5, size=1)
y = z*x + 0.1*x**2
return y


###############################################################################
# Implementing this relationship in :mod:`f3dasm` can be done in two ways:
#
#
# 1. Directly using a function
# 2. Providing an object from a custom class that inherits from the :class:`~f3dasm.datageneration.DataGenerator` class.
#
# Using a function directly
# -------------------------
#
# We can use the function :func:`y(x)` directly as the data generator. We will demonstrate this in the following example code:
#
#
# In order to create an :class:`~f3dasm.ExperimentData` object, we have to first create a domain
domain = Domain()
domain.add_float('x', low=0., high=100.)

###############################################################################
# For demonstration purposes, we will generate a dataset of stopping distances for velocities between 3 and 83 m/s.

N = 33 # number of points to generate
Data_x = np.linspace(3, 83, 100)

###############################################################################
# We can construct an :class:`~f3dasm.ExperimentData` object with the :class:`~f3dasm.design.Domain` and the numpy array:

experiment_data = ExperimentData(input_data=Data_x, domain=domain)
print(experiment_data)

###############################################################################
# As you can see, the ExperimentData object has been created successfully and the jobs have the label 'open'.
# This means that the output has not been generated yet. We can now compute the stopping distance by calling the :meth:`~f3dasm.ExperimentData.evaluate` method:
# We have to provide the function as the ``data_generator`` argument and provide name of the return value as the ``output_names`` argument:

experiment_data.evaluate(data_generator=y, output_names=['y'])

arr_in, arr_out = experiment_data.to_numpy()

fig, ax = plt.subplots()
ax.scatter(arr_in, arr_out.flatten(), s=2)
_ = ax.set_xlabel('Car velocity ($m/s$)')
_ = ax.set_ylabel('Stopping distance ($m$)')

###############################################################################
# The experiments have been evaluated and the jobs value has been set to 'finished'

print(experiment_data)

###############################################################################
#
# Using the DataGenerator class
# -----------------------------
#
# We can also implement the data generator as a class that inherits from the :class:`~f3dasm.datageneration.DataGenerator` class.
# This allows for more flexibility and control over the data generation process.

experiment_data_class = ExperimentData(input_data=Data_x, domain=domain)

###############################################################################
# The custom data generator class should have an :meth:`~f3dasm.datageneration.DataGenerator.execute` method.
# In this method, we can access the experiment using the :attr:`~f3dasm.datageneration.DataGenerator.experiment_sample` attribute.
# We can store the output of the data generation process using the :meth:`~f3dasm.datageneration.DataGenerator.experiment_sample.store` method.


class CarStoppingDistance(DataGenerator):
def __init__(self, mu_z: float, sigma_z: float):
self.mu_z = mu_z
self.sigma_z = sigma_z

def execute(self):
x = self.experiment_sample.get('x')
z = norm.rvs(self.mu_z, self.sigma_z, size=1)
y = z*x + 0.1*x**2
self.experiment_sample.store(object=y, name='y', to_disk=False)

###############################################################################
# We create an object of the :class:`~CarStoppingDistance` class and pass it to the :meth:`~f3dasm.ExperimentData.evaluate` method:


car_stopping_distance = CarStoppingDistance(mu_z=1.5, sigma_z=0.5)
experiment_data_class.evaluate(data_generator=car_stopping_distance)

print(experiment_data_class)
6 changes: 3 additions & 3 deletions examples/domain/README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Domain
------
Design of Experiments
---------------------

Below is a gallery of examples
Examples that use the :mod:`f3dasm.design` module to create a design of experiments
3 changes: 2 additions & 1 deletion examples/experimentdata/README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
ExperimentData
--------------

Below is a gallery of examples
Examples that use the :class:`~f3dasm.ExperimentData` object to control
the data-driven process.
36 changes: 36 additions & 0 deletions examples/experimentdata/plot_experimentdata.py
Original file line number Diff line number Diff line change
Expand Up @@ -100,3 +100,39 @@
#
# When the output to an ExperimentData object is provided, the job will be set to finished,
# as the output data is considerd the result of the experiment.
#
# Adding data after constructing
# ------------------------------
#
# If you have constructed your :class:`~f3dasm.ExperimentData` object,
# you can add ``input_data``, ``output_data``, a ``domain`` or the ``project_dir`` using the :meth:`~f3dasm.ExperimentData.add` method:

new_data = pd.DataFrame({
'x0': [1.5, 1.7],
'x1': [1.3, 1.9]
})
experimentdata.add(input_data=new_data, domain=domain)
print(experimentdata)

###############################################################################
# Exporting the data to various formats
# -------------------------------------
#
# You can convert the input- and outputdata of your data-driven process to other well-known datatypes:
#
# * :meth:`~f3dasm.ExperimentData.to_numpy`; creates a tuple of two :class:`~numpy.ndarray` objects containing the input- and outputdata.

arr_input, arr_output = experimentdata.to_numpy()
print(arr_input)

###############################################################################
# * :meth:`~f3dasm.ExperimentData.to_xarray`; creates a :class:`~xarray.Dataset` object containing the input- and outputdata.

ds = experimentdata.to_xarray()
print(ds)

###############################################################################
# * :meth:`~f3dasm.ExperimentData.to_pandas`; creates a tuple of two :class:`~pd.DataFrame` object containing the input- and outputdata.

df_input, df_output = experimentdata.to_pandas()
print(df_input)
2 changes: 1 addition & 1 deletion examples/hydra/README.rst
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
Integration with hydra
----------------------

Below is a gallery of examples
Examples that integrate the :mod:`f3dasm` package with the configuration manager `hydra <https://hydra.cc/>`_
Loading

0 comments on commit 0e3aa97

Please sign in to comment.