From f4ef01df16dcf778fea8612e969283155ab77a43 Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Mon, 17 Apr 2023 21:21:54 +0900 Subject: [PATCH 1/7] Add docstring for ChoiceWidget --- optuna_dashboard/_form_widget.py | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) diff --git a/optuna_dashboard/_form_widget.py b/optuna_dashboard/_form_widget.py index e78ee4fc..d983bf5d 100644 --- a/optuna_dashboard/_form_widget.py +++ b/optuna_dashboard/_form_widget.py @@ -60,12 +60,34 @@ if TYPE_CHECKING: @dataclass class ChoiceWidget: + """A widget representing a choice with associated values. + + Attributes: + choices: A list of strings representing the available choices. + values: A list of float values associated with each choice. + description: A description of the widget. Defaults to None. + user_attr_key: The key used by `register_user_attr_form_widgets`. + Form output is saved as `trial.user_attrs[user_attr_key]`. Defaults to None. + + Example: + .. code-block:: python + + choice_widget = ChoiceWidget( + choices=["A", "B", "C"], values=[1.0, 2.0, 3.0], description="Choose one" + ) + """ + choices: list[str] values: list[float] description: Optional[str] = None user_attr_key: Optional[str] = None def to_dict(self) -> ChoiceWidgetJSON: + """Convert the ChoiceWidget object to a dictionary. + + Returns: + ChoiceWidgetJSON: A dictionary representing the ChoiceWidget object. + """ return { "type": "choice", "description": self.description, From 468ca016afe6c6bfd3f0bc13265f0a15da9c8f98 Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Wed, 26 Apr 2023 14:39:08 +0900 Subject: [PATCH 2/7] Use napoleon extension in conf.py --- docs/conf.py | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/conf.py b/docs/conf.py index 35050f11..9056f0db 100644 --- a/docs/conf.py +++ b/docs/conf.py @@ -23,6 +23,7 @@ extensions = [ "sphinx.ext.autodoc", "sphinx.ext.doctest", "sphinx.ext.autosummary", + "sphinx.ext.napoleon", ] templates_path = ["_templates"] From 767aaa92482d4c29090d2f4ab79afc6f4938719a Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Wed, 26 Apr 2023 15:12:02 +0900 Subject: [PATCH 3/7] Add docstring for SliderWidget, TextInputWidget, and ObjectiveUserAttrRef --- optuna_dashboard/_form_widget.py | 64 +++++++++++++++++++++++++++++++- 1 file changed, 63 insertions(+), 1 deletion(-) diff --git a/optuna_dashboard/_form_widget.py b/optuna_dashboard/_form_widget.py index d983bf5d..2b97af48 100644 --- a/optuna_dashboard/_form_widget.py +++ b/optuna_dashboard/_form_widget.py @@ -62,7 +62,7 @@ if TYPE_CHECKING: class ChoiceWidget: """A widget representing a choice with associated values. - Attributes: + Args: choices: A list of strings representing the available choices. values: A list of float values associated with each choice. description: A description of the widget. Defaults to None. @@ -109,6 +109,23 @@ class ChoiceWidget: @dataclass class SliderWidget: + """A widget representing a slider for selecting a value within a range. + + Args: + min: The minimum value of the slider. + max: The maximum value of the slider. + step: The step size for the slider. Defaults to None. + labels: A list of tuples containing value and label for the slider. Defaults to None. + description: A description for the slider. Defaults to None. + user_attr_key: The key used by `register_user_attr_form_widgets`. + Form output is saved as `trial.user_attrs[user_attr_key]`. Defaults to None. + + Example: + .. code-block:: python + + slide_widget = SliderWidget(min=0, max=10, step=1, description="Example slider") + """ + min: float max: float step: Optional[float] = None @@ -117,6 +134,11 @@ class SliderWidget: user_attr_key: Optional[str] = None def to_dict(self) -> SliderWidgetJSON: + """Convert the SliderWidget instance to a dictionary. + + Returns: + SliderWidgetJSON: A dictionary representation of the SliderWidget instance. + """ labels: Optional[list[SliderWidgetLabel]] = None if self.labels is not None: labels = [{"value": value, "label": label} for value, label in self.labels] @@ -148,10 +170,30 @@ class SliderWidget: @dataclass class TextInputWidget: + """ + A text input widget class that defines a text input field. + + Args: + description: A description of the text input field. + user_attr_key: The key used by `register_user_attr_form_widgets`. + Form output is saved as `trial.user_attrs[user_attr_key]`. Defaults to None. + + Example: + .. code-block:: python + + text_input = TextInputWidget(description="Text Input Example") + """ + description: Optional[str] = None user_attr_key: Optional[str] = None def to_dict(self) -> TextInputWidgetJSON: + """ + Converts the TextInputWidget instance to a dictionary representation. + + Returns: + TextInputWidgetJSON: The dictionary representation of the TextInputWidget instance. + """ return { "type": "text", "description": self.description, @@ -169,9 +211,29 @@ class TextInputWidget: @dataclass class ObjectiveUserAttrRef: + """ + A class representing a reference to a value of `trial.user_attrs`. + When combined with `register_objective_form_widgets`, users can tell values that are registered to + `trial.user_attrs` during the human-in-the-loop optimization. + + Args: + key: The key of `trial.user_attrs` being referenced. + + Example: + .. code-block:: python + + user_attr_ref = ObjectiveUserAttrRef(key="key") + """ + key: str def to_dict(self) -> UserAttrRefJSON: + """ + Converts the ObjectiveUserAttrRef instance to a dictionary representation. + + Returns: + UserAttrRefJSON: The dictionary representation of the ObjectiveUserAttrRef instance. + """ return { "type": "user_attr", "key": self.key, From 6edc285f56cfc4fb40d6ed5a088f51424188ca67 Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Wed, 26 Apr 2023 15:17:03 +0900 Subject: [PATCH 4/7] Add import for examples --- optuna_dashboard/_form_widget.py | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/optuna_dashboard/_form_widget.py b/optuna_dashboard/_form_widget.py index 2b97af48..d9a80b69 100644 --- a/optuna_dashboard/_form_widget.py +++ b/optuna_dashboard/_form_widget.py @@ -72,6 +72,9 @@ class ChoiceWidget: Example: .. code-block:: python + from optuna_dashboard import ChoiceWidget + + choice_widget = ChoiceWidget( choices=["A", "B", "C"], values=[1.0, 2.0, 3.0], description="Choose one" ) @@ -123,6 +126,9 @@ class SliderWidget: Example: .. code-block:: python + from optuna_dashboard import SliderWidget + + slide_widget = SliderWidget(min=0, max=10, step=1, description="Example slider") """ @@ -181,6 +187,9 @@ class TextInputWidget: Example: .. code-block:: python + from optuna_dashboard import TextInputWidget + + text_input = TextInputWidget(description="Text Input Example") """ @@ -222,6 +231,9 @@ class ObjectiveUserAttrRef: Example: .. code-block:: python + from optuna_dashboard import ObjectiveUserAttrRef + + user_attr_ref = ObjectiveUserAttrRef(key="key") """ From 873c4033eb39b52351aeac8f707ade8a608c6d1a Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Wed, 26 Apr 2023 15:43:41 +0900 Subject: [PATCH 5/7] Add docstring for register_*_form_widgets --- docs/api.rst | 1 + optuna_dashboard/_form_widget.py | 80 ++++++++++++++++++++++++++++++++ 2 files changed, 81 insertions(+) diff --git a/docs/api.rst b/docs/api.rst index 53b59d24..061e93fc 100644 --- a/docs/api.rst +++ b/docs/api.rst @@ -23,6 +23,7 @@ Human-in-the-loop :nosignatures: optuna_dashboard.register_objective_form_widgets + optuna_dashboard.register_user_attr_form_widgets optuna_dashboard.dict_to_form_widget optuna_dashboard.ChoiceWidget optuna_dashboard.SliderWidget diff --git a/optuna_dashboard/_form_widget.py b/optuna_dashboard/_form_widget.py index d9a80b69..622295e7 100644 --- a/optuna_dashboard/_form_widget.py +++ b/optuna_dashboard/_form_widget.py @@ -291,6 +291,45 @@ def dict_to_form_widget(d: dict[str, Any]) -> ObjectiveFormWidget: def register_objective_form_widgets( study: optuna.Study, widgets: list[ObjectiveFormWidget] ) -> None: + """ + Register a list of form widgets to an Optuna study. + + Submitted values to the forms are told as each trial's objective values. + + Args: + study: The Optuna study object to register the form widgets for. + widgets: A list of ObjectiveFormWidget objects to be registered in the study. + + Raises: + ValueError: If the length of study directions is not equal to the length of widgets. + Warning: If any widget has `user_attr_key` specified, but it will not be used. + + Examples: + .. code-block:: python + + import optuna + from optuna_dashboard import ChoiceWidget, SliderWidget + from optuna_dashboard import register_objective_form_widgets + + + study = optuna.create_study() + register_objective_form_widgets( + study, + widgets=[ + ObjectiveChoiceWidget( + choices=["Good 👍", "Bad 👎"], + values=[-1, 1], + description="Please input your score!", + ), + ObjectiveSliderWidget( + min=1, + max=10, + step=1, + description="Higher is better.", + ), + ], + ) + """ if len(study.directions) != len(widgets): raise ValueError("The length of actions must be the same with the number of objectives.") if any( @@ -307,6 +346,47 @@ def register_objective_form_widgets( def register_user_attr_form_widgets( study: optuna.Study, widgets: list[ObjectiveFormWidget] ) -> None: + """ + Register a list of form widgets to an Optuna study. + + Submitted values to the forms are registered as each trial's user_attrs. + + Args: + study: The Optuna study object to register the form widgets for. + widgets: A list of ObjectiveFormWidget objects to be registered in the study. + + Raises: + ValueError: If an ObjectiveUserAttrRef is specified or if `user_attr_key` is not specified. + ValueError: If `user_attr_key` is not unique for each widget. + + Examples: + .. code-block:: python + + import optuna + from optuna_dashboard import ChoiceWidget, SliderWidget + from optuna_dashboard import register_user_attr_form_widgets + + + study = optuna.create_study() + register_user_attr_form_widgets( + study, + widgets=[ + ChoiceWidget( + choices=["Good 👍", "Bad 👎"], + values=[-1, 1], + description="Please input your score!", + user_attr_key="hitl/choice", + ), + SliderWidget( + min=1, + max=10, + step=1, + description="Higher is better.", + user_attr_key="hitl/slider", + ), + ], + ) + """ user_attr_keys = set() widget_dicts: list[Union[ChoiceWidgetJSON, SliderWidgetJSON, TextInputWidgetJSON]] = [] for w in widgets: From df31abe7aaeec3a0de40bfe3b0a2d806b18e0e25 Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Wed, 26 Apr 2023 17:26:18 +0900 Subject: [PATCH 6/7] Fix flake8 --- optuna_dashboard/_form_widget.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/_form_widget.py b/optuna_dashboard/_form_widget.py index ee79e52b..2d6809ab 100644 --- a/optuna_dashboard/_form_widget.py +++ b/optuna_dashboard/_form_widget.py @@ -224,8 +224,8 @@ class TextInputWidget: class ObjectiveUserAttrRef: """ A class representing a reference to a value of `trial.user_attrs`. - When combined with `register_objective_form_widgets`, users can tell values that are registered to - `trial.user_attrs` during the human-in-the-loop optimization. + When combined with `register_objective_form_widgets`, users can tell values that are + registered to `trial.user_attrs` during the human-in-the-loop optimization. Args: key: The key of `trial.user_attrs` being referenced. From 93f02512f4a29480da6bd57c823b37a1c75afe09 Mon Sep 17 00:00:00 2001 From: Kenshin Abe Date: Thu, 27 Apr 2023 16:15:34 +0900 Subject: [PATCH 7/7] Do not show __init__ method in document --- docs/_templates/autosummary/class.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 docs/_templates/autosummary/class.rst diff --git a/docs/_templates/autosummary/class.rst b/docs/_templates/autosummary/class.rst new file mode 100644 index 00000000..0c48f07c --- /dev/null +++ b/docs/_templates/autosummary/class.rst @@ -0,0 +1,20 @@ +{% extends "!autosummary/class.rst" %} + +{# +Ref: https://github.com/optuna/optuna/blob/41045cef7a8f5d0e5e4026bf0c5c649b269fc312/docs/source/_templates/autosummary/class.rst +An autosummary template to exclude the class constructor (__init__) +which doesn't contain any docstring in Optuna Dashboard. +#} + +{% block methods %} + {% set methods = methods | select("ne", "__init__") | list %} + {% if methods %} + .. rubric:: Methods + + .. autosummary:: + {% for item in methods %} + ~{{ name }}.{{ item }} + {%- endfor %} + {% endif %} + +{% endblock %}