Skip to content

Commit

Permalink
changed Design to ExperimentSample
Browse files Browse the repository at this point in the history
  • Loading branch information
mpvanderschelling committed Sep 8, 2023
1 parent f2d39f0 commit 190cba3
Show file tree
Hide file tree
Showing 34 changed files with 232 additions and 1,396 deletions.
2 changes: 1 addition & 1 deletion docs/source/index.rst
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ f3dasm
rst_doc_files/classes/design/parameters
rst_doc_files/classes/design/domain
rst_doc_files/classes/design/experimentdata
rst_doc_files/classes/design/design
rst_doc_files/classes/design/experimentsample
rst_doc_files/classes/sampling/sampling

.. toctree::
Expand Down
29 changes: 15 additions & 14 deletions docs/source/rst_doc_files/classes/datageneration/datagenerator.rst
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ Datagenerator
=============

The :class:`~f3dasm.datageneration.datagenerator.DataGenerator` class is the main class of the :mod:`~f3dasm.datageneration` module.
It is used to generate :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data` for the :class:`~f3dasm.design.experimentdata.ExperimentData` by taking a :class:`~f3dasm.design.design.Design` object.
It is used to generate :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data` for the :class:`~f3dasm.design.experimentdata.ExperimentData` by taking a :class:`~f3dasm.design.experimentsample.ExperimentSample` object.

The :class:`~f3dasm.datageneration.datagenerator.DataGenerator` can serve as the interface between the
:class:`~f3dasm.design.experimentdata.ExperimentData` object and any third-party simulation software.
Expand All @@ -17,7 +17,7 @@ The :class:`~f3dasm.datageneration.datagenerator.DataGenerator` can serve as the
Creating a data-generator
-------------------------

In order to run your simulator on each of the :class:`~f3dasm.design.design.Design` of your :class:`~f3dasm.design.experimentdata.ExperimentData`, you follow these steps:
In order to run your simulator on each of the :class:`~f3dasm.design.experimentsample.ExperimentSample` of your :class:`~f3dasm.design.experimentdata.ExperimentData`, you follow these steps:
In this case, we are utilizing a one of the :ref:`benchmark-functions` to mock a simulator.

1. Construct the :class:`~f3dasm.datageneration.datagenerator.DataGenerator` object.
Expand All @@ -38,11 +38,11 @@ In this case, we are utilizing a one of the :ref:`benchmark-functions` to mock a
Any key-word arguments that need to be passed down to the :class:`~f3dasm.datageneration.datagenerator.DataGenerator` :code:`__call__` function can be passed in the :code:`kwargs` argument of the :meth:`~f3dasm.design.experimentdata.ExperimentData.run` function.


There are three methods available of handeling the :class:`~f3dasm.design.design.Design` objects:
There are three methods available of handeling the :class:`~f3dasm.design.experimentsample.ExperimentSample` objects:

* :code:`sequential`: regular for-loop over each of the :class:`~f3dasm.design.design.Design` objects in order
* :code:`parallel`: utilizing the multiprocessing capabilities, each :class:`~f3dasm.design.design.Design` object is run in a separate core
* :code:`cluster`: utilizing the multiprocessing capabilities, each :class:`~f3dasm.design.design.Design` object is run in a separate node. After termination of a design, the node will automatically pick the next available design. More information on this mode can be found in the :ref:`cluster-mode` section.
* :code:`sequential`: regular for-loop over each of the :class:`~f3dasm.design.experimentsample.ExperimentSample` objects in order
* :code:`parallel`: utilizing the multiprocessing capabilities, each :class:`~f3dasm.design.experimentsample.ExperimentSample` object is run in a separate core
* :code:`cluster`: utilizing the multiprocessing capabilities, each :class:`~f3dasm.design.experimentsample.ExperimentSample` object is run in a separate node. After completion of an sample, the node will automatically pick the next available sample. More information on this mode can be found in the :ref:`cluster-mode` section.


Implemented data-generators
Expand Down Expand Up @@ -73,7 +73,7 @@ Create your own data-generator
In order to use your own simulator or script, you need to comply with either one of the following options:

* Create a class that inherits from the :class:`~f3dasm.datageneration.datagenerator.DataGenerator` class and implement the methods.
* Create a function that takes a :class:`~f3dasm.design.design.Design` object as an argument (and returns a :class:`~f3dasm.design.design.Design`).
* Create a function that takes a :class:`~f3dasm.design.experimentsample.ExperimentSample` object as an argument (and returns a :class:`~f3dasm.design.experimentsample.ExperimentSample`).


Inherit from DataGenerator
Expand Down Expand Up @@ -111,13 +111,13 @@ An example is given in the following code block:
def execute(self, any_argument: str, **kwargs) -> None:
# Retrieve parameters
parameter_1 = self.design['parameter1']
parameter_1 = self.experiment_sample['parameter1']
# Run a simulation
...
# Store the results
self.design['result'] = result
self.experiment_sample['result'] = result
def post_process(self, any_post_process_arg: str, **kwargs) -> None:
...
Expand All @@ -133,18 +133,19 @@ Create a data-generator from a functional approach
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The functional approach is a bit more flexible, as it allows you to use any function
that takes a :class:`~f3dasm.design.design.Design` object as an argument, and returns a :class:`~f3dasm.design.design.Design` object.
that takes a :class:`~f3dasm.design.experimentsample.ExperimentSample` object as an argument, and returns a :class:`~f3dasm.design.experimentsample.ExperimentSample` object.

.. note::

The :class:`~f3dasm.datageneration.datagenerator.DataGenerator` class is a wrapper around the functional approach.


.. code-block:: python
from f3dasm import Design
from f3dasm import ExperimentSample
def my_function(design: f3dasm.Design, some_kwarg: int):
# do something with the design
return design
def my_function(experiment_sample: f3dasm.ExperimentSample, some_kwarg: int):
# do something with the sample
return experiment_sample
experimentdata.run(my_function, method='sequential', kwargs={'some_kwarg': 1})
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,12 @@ Benchmarkfunction are a submodule of the :code:`f3dasm.datageneration` module un
Creating the function
^^^^^^^^^^^^^^^^^^^^^

First we make a 2-dimensional continous design space with the helper function :func:`~f3dasm.design.design.make_nd_continuous_design`:
First we make a 2-dimensional continous domain with the helper function :func:`~f3dasm.design.design.make_nd_continuous_design`:

.. code-block:: python
bounds = np.array([[-1.0, 1.0], [-1.0, 1.0]])
design = f3dasm.make_nd_continuous_design(bounds=bounds, dimensions=2)
domain = f3dasm.make_nd_continuous_design(bounds=bounds, dimensions=2)
Then we create an object of the :class:`~f3dasm.functions.pybenchfunction.Sphere` class, specifying the :attr:`~f3dasm.functions.pybenchfunction.PybenchFunction.dimensionality`:

Expand Down
2 changes: 1 addition & 1 deletion docs/source/rst_doc_files/classes/design/domain.rst
Original file line number Diff line number Diff line change
Expand Up @@ -113,4 +113,4 @@ We can make easily make a :math:`n`-dimensional continous domain with the helper
.. code-block:: python
bounds = np.array([[-1.0, 1.0], [-1.0, 1.0]])
design = f3dasm.make_nd_continuous_domain(bounds=bounds, dimensionality=2)
domain = f3dasm.make_nd_continuous_domain(bounds=bounds, dimensionality=2)
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
Design
======

A :class:`~f3dasm.design.design.Design` object contains a single realization of the design-of-experiment in :class:`~f3dasm.design.experimentdata.ExperimentData`.
A :class:`~f3dasm.design.experimentsample.ExperimentSample` object contains a single realization of the design-of-experiment in :class:`~f3dasm.design.experimentdata.ExperimentData`.

.. image:: ../../../img/f3dasm-design.png
:alt: Design
Expand All @@ -11,81 +11,81 @@ A :class:`~f3dasm.design.design.Design` object contains a single realization of
|
.. note::
A :class:`~f3dasm.design.design.Design` is not constructed manually, but created inside the ExperimentData when it is required by internal processes.
The main use of the :class:`~f3dasm.design.design.Design` is to pass it to your own functions and scripts to extract design variables and store output variables.
A :class:`~f3dasm.design.experimentsample.ExperimentSample` is not constructed manually, but created inside the ExperimentData when it is required by internal processes.
The main use of the :class:`~f3dasm.design.experimentsample.ExperimentSample` is to pass it to your own functions and scripts to extract design variables and store output variables.



For each of the experiments in the :class:`~f3dasm.design.experimentdata.ExperimentData`, a :class:`~f3dasm.design.design.Design` object can be created.
This object contains the input and output parameters of a single realization of the :class:`~f3dasm.design.experimentdata.ExperimentData`, as well as the index number of the experiment (:attr:`~f3dasm.design.design.Design.job_number`).
For each of the experiments in the :class:`~f3dasm.design.experimentdata.ExperimentData`, a :class:`~f3dasm.design.experimentsample.ExperimentSample` object can be created.
This object contains the input and output parameters of a single realization of the :class:`~f3dasm.design.experimentdata.ExperimentData`, as well as the index number of the experiment (:attr:`~f3dasm.design.experimentsample.ExperimentSample.job_number`).

.. code-block:: python
from f3dasm import Design
from f3dasm import ExperimentSample
def my_function(design: Design, **kwargs):
parameter1 = design['param_1']
parameter2 = design['param_2']
job_number = design.job_number
def my_function(experiment_sample: ExperimentSample, **kwargs):
parameter1 = experiment_sample['param_1']
parameter2 = experiment_sample['param_2']
job_number = experiment_sample.job_number
... # Your own program
design['output_1'] = output
return design
experiment_sample['output_1'] = output
return experiment_sample
A function with a signature like :code:`my_function` can be used as a callable in the :meth:`~f3dasm.design.experimentdata.ExperimentData.run` method to iterate over every design in the :class:`~f3dasm.design.experimentdata.ExperimentData`.
A function with a signature like :code:`my_function` can be used as a callable in the :meth:`~f3dasm.design.experimentdata.ExperimentData.run` method to iterate over every sample in the :class:`~f3dasm.design.experimentdata.ExperimentData`.

.. note::
In order to use :code:`my_function` within :mod:`f3dasm` workflow, the first argument needs to be a :class:`~f3dasm.design.design.Design` object.
In order to use :code:`my_function` within :mod:`f3dasm` workflow, the first argument needs to be a :class:`~f3dasm.design.experimentsample.ExperimentSample` object.
The function can have any number of additional arguments, which will be passed to the function when it is called.
Lastly, the :class:`~f3dasm.design.design.Design` must be returned.
Lastly, the :class:`~f3dasm.design.experimentsample.ExperimentSample` must be returned.

Extract parameters from a design
--------------------------------
Extract parameters from a experiment sample
-------------------------------------------

Input parameters of a design can be accessed using the :code:`[]` operator, with the name of the parameter as the key.
Only input parameters of the design can be accessed this way, and an error will be raised if the key is not found.
Input parameters of an experiment sample can be accessed using the :code:`[]` operator, with the name of the parameter as the key.
Only input parameters of the experiment sample can be accessed this way, and an error will be raised if the key is not found.

.. code-block:: python
>>> design['param_1']
>>> experiment_sample['param_1']
0.0249
The job_number of the design can be accessed using the :attr:`~f3dasm.design.design.Design.job_number` attribute and is zero-indexed.
The job_number of the experiment sample can be accessed using the :attr:`~f3dasm.design.experimentsample.ExperimentSaple.job_number` attribute and is zero-indexed.

.. code-block:: python
>>> design.job_number
>>> experiment_sample.job_number
0
The input and output parameters of a design can be extracted as a tuple of numpy arrays with the :meth:`~f3dasm.design.Design.to_numpy` method.
The input and output parameters of an experiment sample can be extracted as a tuple of numpy arrays with the :meth:`~f3dasm.experimentsample.ExperimentSample.to_numpy` method.

.. code-block:: python
>>> design.to_numpy()
>>> experiment_sample.to_numpy()
(np.array([0.0249, 0.034, 0.100]), np.array([]))
Storing output parameters to the design
---------------------------------------
Storing output parameters to the experiment sample
--------------------------------------------------

After running your calculation, you can store the result back into the design in two ways:
After running your calculation, you can store the result back into the experiment sample in two ways:

* Singular values and small lists can be stored directly to the :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data`
* Large objects can be stored to disk with the :meth:`f3dasm.design.design.Design.store` method.
* Large objects can be stored to disk with the :meth:`f3dasm.design.experimentsample.ExperimentSample.store` method.

Single values or small lists
^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Single values or small lists can be stored to the design using the :code:`[]` operator, with the name of the parameter as the key.
Single values or small lists can be stored to the :class:`~f3dasm.design.experimentdata.ExperimentData` using the :code:`[]` operator, with the name of the parameter as the key.
This will create a new output parameter if the parameter name is not found in :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data` of the :class:`~f3dasm.design.experimentdata.ExperimentData`.

.. code-block:: python
>>> design['output_1'] = 0.123
>>> design['output_2'] = [0.123, 0.456, 0.789]
>>> design['output_3'] = 'Hello world'
>>> experiment_sample['output_1'] = 0.123
>>> experiment_sample['output_2'] = [0.123, 0.456, 0.789]
>>> experiment_sample['output_3'] = 'Hello world'
All built-in types are supported for storing to the design this way. Array-like data such as numpy arrays and pandas dataframes are **not** supported and will raise an error.
All built-in types are supported for storing to the :class:`~f3dasm.design.experimentdata.ExperimentData` this way. Array-like data such as numpy arrays and pandas dataframes are **not** supported and will raise an error.

.. note::
Outputs stored directly to the :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data` will be stored within the :class:`~f3dasm.design.experimentdata.ExperimentData` object.
Expand All @@ -94,13 +94,13 @@ All built-in types are supported for storing to the design this way. Array-like
Large objects and array-like data
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

In order to store large objects or array-like data, the :meth:`~f3dasm.design.design.Design.store` method can be used. A reference (:code:`Path`) will be saved to the :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data`.
In order to store large objects or array-like data, the :meth:`~f3dasm.design.experimentsample.ExperimentSample.store` method can be used. A reference (:code:`Path`) will be saved to the :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data`.

.. code-block:: python
>>> design.store('output_1', my_large_object)
>>> experiment_sample.store('output_1', my_large_object)
:mod:`f3dasm` will automatically create a new directory for each output parameter and store the object with a generated filename referencing the :attr:`~f3dasm.design.design.Design.job_number` of the design.
:mod:`f3dasm` will automatically create a new directory for each output parameter and store the object with a generated filename referencing the :attr:`~f3dasm.design.experimentsample.ExperimentSample.job_number` of the design.

.. code-block:: none
:caption: Directory Structure
Expand All @@ -120,13 +120,13 @@ In the :attr:`~f3dasm.design.experimentdata.ExperimentData.output_data`, a refer

.. code-block:: python
>>> design['output_1_path']
>>> experiment_sample['output_1_path']
'my_project/output_1/0.npy'
:mod:`f3dasm` has built-in storing functions for numpy arrays, pandas DataFrames and xarray DataArrays and Datasets.
For any other type of object, you can provide a storing function to the :meth:`~f3dasm.design.design.Design.store` method call:
For any other type of object, you can provide a storing function to the :meth:`~f3dasm.design.experimentsample.ExperimentSample.store` method call:

* The arguments must be the object itself and the path that it should store to
* The function should store the object to disk.
Expand All @@ -141,8 +141,8 @@ You can take the following function as an example:
return '.npy'
After defining the storing function, it can be used as a callable in the :meth:`~f3dasm.design.design.Design.store` method:
After defining the storing function, it can be used as a callable in the :meth:`~f3dasm.design.experimentsample.ExperimentSample.store` method:

.. code-block:: python
>>> design.store('output_1', my_large_object, numpy_storing_function)
>>> experiment_sample.store('output_1', my_large_object, numpy_storing_function)
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ Optimizer
The :class:`~f3dasm.optimization.optimizer.Optimizer` class is used to find the minimum of a particular quantity of interest through an iterative fashion.

* The :meth:`~f3dasm.optimization.optimizer.Optimizer.update_step` method takes a :class:`~f3dasm.datageneration.functions.function.Function` object and outputs a tuple containing the position and evaluated value of the next iteration.
* The :meth:`~f3dasm.optimization.optimizer.Optimizer.iterate` method is used to start the optimization process. It takes the number of iterations and a :class:`~f3dasm.base.function.Function` object as arguments. For every iteration, the :meth:`~f3dasm.optimization.optimizer.Optimizer.update_step` method is called and the results are stored as new :class:`~f3dasm.design.design.Design` in the :class:`~f3dasm.design.experimentdata.ExperimentData` object.
* The :meth:`~f3dasm.optimization.optimizer.Optimizer.iterate` method is used to start the optimization process. It takes the number of iterations and a :class:`~f3dasm.base.function.Function` object as arguments. For every iteration, the :meth:`~f3dasm.optimization.optimizer.Optimizer.update_step` method is called and the results are stored as new :class:`~f3dasm.design.experimentsample.ExperimentSample` in the :class:`~f3dasm.design.experimentdata.ExperimentData` object.


.. image:: ../../../img/f3dasm-optimizer.png
Expand Down
Loading

0 comments on commit 190cba3

Please sign in to comment.