We are currently solving the following issue within our repository. Here is the issue text:
--- BEGIN ISSUE ---
TensorBoard integration fails if None is suggested
### Expected behavior

`Optuna` with `TensorBoardCallback` should allow `None` as a suggestion.

### Environment

- Optuna version:3.2.0
- Python version:3.9.13
- OS:Linux-5.4.228-132.418.amzn2.x86_64-x86_64-with-glibc2.31

### Error messages, stack traces, or logs

```shell
File /opt/python3.9/site-packages/tensorboard/plugins/hparams/summary_v2.py:507, in Discrete.__init__(self, values, dtype)
    505         raise ValueError("Empty domain with no dtype specified")
    506 if dtype not in (int, float, bool, str):
--> 507     raise ValueError("Unknown dtype: %r" % (dtype,))
    508 self._dtype = dtype
    509 for value in self._values:

ValueError: Unknown dtype: <class 'NoneType'>
```
```


### Steps to reproduce

1. If I run this
```python
import optuna
from optuna.integration.tensorboard import TensorBoardCallback

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score

X, y = make_classification(n_samples=1000, n_features=4,
                           n_informative=2, n_redundant=0,
                           random_state=0, shuffle=False)

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.33, random_state=0)

def objective(trial):
    params = {
        'max_leaf_nodes': trial.suggest_categorical('max_leaf_nodes', [None, 2, 5, 10, 20]),
    }
    clf = RandomForestClassifier()
    clf.fit(X_train, y_train)

    y_val_pred = clf.predict(X_val)
    f1 = f1_score(y_val, y_val_pred)
    return f1

tensorboard_callback = TensorBoardCallback("logs/", metric_name="f1")
study = optuna.create_study(direction="minimize")

study.optimize(objective, n_trials=10, timeout=600, callbacks=[tensorboard_callback])
```

It would return an error above.

2. If I don't have `None` in the suggestion or not use `TensorBoardCallback` then it would work fine.

### Additional context (optional)

_No response_
TensorBoard integration fails if None is suggested
### Expected behavior

`Optuna` with `TensorBoardCallback` should allow `None` as a suggestion.

### Environment

- Optuna version:3.2.0
- Python version:3.9.13
- OS:Linux-5.4.228-132.418.amzn2.x86_64-x86_64-with-glibc2.31

### Error messages, stack traces, or logs

```shell
File /opt/python3.9/site-packages/tensorboard/plugins/hparams/summary_v2.py:507, in Discrete.__init__(self, values, dtype)
    505         raise ValueError("Empty domain with no dtype specified")
    506 if dtype not in (int, float, bool, str):
--> 507     raise ValueError("Unknown dtype: %r" % (dtype,))
    508 self._dtype = dtype
    509 for value in self._values:

ValueError: Unknown dtype: <class 'NoneType'>
```
```


### Steps to reproduce

1. If I run this
```python
import optuna
from optuna.integration.tensorboard import TensorBoardCallback

from sklearn.ensemble import RandomForestClassifier
from sklearn.datasets import make_classification
from sklearn.model_selection import train_test_split
from sklearn.metrics import f1_score

X, y = make_classification(n_samples=1000, n_features=4,
                           n_informative=2, n_redundant=0,
                           random_state=0, shuffle=False)

X_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.33, random_state=0)

def objective(trial):
    params = {
        'max_leaf_nodes': trial.suggest_categorical('max_leaf_nodes', [None, 2, 5, 10, 20]),
    }
    clf = RandomForestClassifier()
    clf.fit(X_train, y_train)

    y_val_pred = clf.predict(X_val)
    f1 = f1_score(y_val, y_val_pred)
    return f1

tensorboard_callback = TensorBoardCallback("logs/", metric_name="f1")
study = optuna.create_study(direction="minimize")

study.optimize(objective, n_trials=10, timeout=600, callbacks=[tensorboard_callback])
```

It would return an error above.

2. If I don't have `None` in the suggestion or not use `TensorBoardCallback` then it would work fine.

### Additional context (optional)

_No response_

--- END ISSUE ---

Below are some code segments, each from a relevant file. One or more of these files may contain bugs.
--- BEGIN FILES ---
Path: `optuna/integration/tensorboard.py`
Content:
```
1 import os
2 from typing import Dict
3 
4 import optuna
5 from optuna._experimental import experimental_class
6 from optuna._imports import try_import
7 
8 
9 with try_import() as _imports:
10     from tensorboard.plugins.hparams import api as hp
11     import tensorflow as tf
12 
13 
14 @experimental_class("2.0.0")
15 class TensorBoardCallback:
16     """Callback to track Optuna trials with TensorBoard.
17 
18     This callback adds relevant information that is tracked by Optuna to TensorBoard.
19 
20     See `the example <https://github.com/optuna/optuna-examples/blob/main/
21     tensorboard/tensorboard_simple.py>`_.
22 
23     Args:
24         dirname:
25             Directory to store TensorBoard logs.
26         metric_name:
27             Name of the metric. Since the metric itself is just a number,
28             `metric_name` can be used to give it a name. So you know later
29             if it was roc-auc or accuracy.
30 
31     """
32 
33     def __init__(self, dirname: str, metric_name: str) -> None:
34         _imports.check()
35         self._dirname = dirname
36         self._metric_name = metric_name
37         self._hp_params: Dict[str, hp.HParam] = {}
38 
39     def __call__(self, study: optuna.study.Study, trial: optuna.trial.FrozenTrial) -> None:
40         if len(self._hp_params) == 0:
41             self._initialization(study)
42         if trial.state != optuna.trial.TrialState.COMPLETE:
43             return
44         trial_value = trial.value if trial.value is not None else float("nan")
45         hparams = {}
46         for param_name, param_value in trial.params.items():
47             if param_name not in self._hp_params:
48                 self._add_distributions(trial.distributions)
49             hparams[self._hp_params[param_name]] = param_value
50         run_name = "trial-%d" % trial.number
51         run_dir = os.path.join(self._dirname, run_name)
52         with tf.summary.create_file_writer(run_dir).as_default():
53             hp.hparams(hparams, trial_id=run_name)  # record the values used in this trial
54             tf.summary.scalar(self._metric_name, trial_value, step=trial.number)
55 
56     def _add_distributions(
57         self, distributions: Dict[str, optuna.distributions.BaseDistribution]
58     ) -> None:
59         supported_distributions = (
60             optuna.distributions.CategoricalDistribution,
61             optuna.distributions.FloatDistribution,
62             optuna.distributions.IntDistribution,
63         )
64 
65         for param_name, param_distribution in distributions.items():
66             if isinstance(param_distribution, optuna.distributions.FloatDistribution):
67                 self._hp_params[param_name] = hp.HParam(
68                     param_name,
69                     hp.RealInterval(float(param_distribution.low), float(param_distribution.high)),
70                 )
71             elif isinstance(param_distribution, optuna.distributions.IntDistribution):
72                 self._hp_params[param_name] = hp.HParam(
73                     param_name,
74                     hp.IntInterval(param_distribution.low, param_distribution.high),
75                 )
76             elif isinstance(param_distribution, optuna.distributions.CategoricalDistribution):
77                 self._hp_params[param_name] = hp.HParam(
78                     param_name,
79                     hp.Discrete(param_distribution.choices),
80                 )
81             else:
82                 distribution_list = [
83                     distribution.__name__ for distribution in supported_distributions
84                 ]
85                 raise NotImplementedError(
86                     "The distribution {} is not implemented. "
87                     "The parameter distribution should be one of the {}".format(
88                         param_distribution, distribution_list
89                     )
90                 )
91 
92     def _initialization(self, study: optuna.Study) -> None:
93         completed_trials = [
94             trial
95             for trial in study.get_trials(deepcopy=False)
96             if trial.state == optuna.trial.TrialState.COMPLETE
97         ]
98         for trial in completed_trials:
99             self._add_distributions(trial.distributions)
100
```
--- END FILES ---

Please first localize the bug based on the issue statement, and then generate a patch according to the `git diff` format fenced by three backticks.

Here is an example:

```diff
diff --git a/examples/server_async.py b/examples/server_async.py
--- a/examples/server_async.py
+++ b/examples/server_async.py
@@ -313,4 +313,4 @@
 
 
 if __name__ == "__main__":
-    asyncio.run(run_async_server("."), debug=True)
+    asyncio.run(run_async_server(), debug=True)
diff --git a/examples/server_sync.py b/examples/server_sync.py
--- a/examples/server_sync.py
+++ b/examples/server_sync.py
@@ -313,5 +313,5 @@
 
 
 if __name__ == "__main__":
-    server = run_sync_server(".")
+    server = run_sync_server()
     server.shutdown()

```

diff --git a/optuna/integration/tensorboard.py b/optuna/integration/tensorboard.py
--- a/optuna/integration/tensorboard.py
+++ b/optuna/integration/tensorboard.py
@@ -4,12 +4,15 @@
 import optuna
 from optuna._experimental import experimental_class
 from optuna._imports import try_import
+from optuna.logging import get_logger
 
 
 with try_import() as _imports:
     from tensorboard.plugins.hparams import api as hp
     import tensorflow as tf
 
+_logger = get_logger(__name__)
+
 
 @experimental_class("2.0.0")
 class TensorBoardCallback:
@@ -46,7 +49,11 @@
         for param_name, param_value in trial.params.items():
             if param_name not in self._hp_params:
                 self._add_distributions(trial.distributions)
-            hparams[self._hp_params[param_name]] = param_value
+            param = self._hp_params[param_name]
+            if isinstance(param.domain, hp.Discrete):
+                hparams[param] = param.domain.dtype(param_value)
+            else:
+                hparams[param] = param_value
         run_name = "trial-%d" % trial.number
         run_dir = os.path.join(self._dirname, run_name)
         with tf.summary.create_file_writer(run_dir).as_default():
@@ -74,9 +81,24 @@
                     hp.IntInterval(param_distribution.low, param_distribution.high),
                 )
             elif isinstance(param_distribution, optuna.distributions.CategoricalDistribution):
+                choices = param_distribution.choices
+                dtype = type(choices[0])
+                if any(not isinstance(choice, dtype) for choice in choices):
+                    _logger.warning(
+                        "Choices contains mixed types, which is not supported by TensorBoard. "
+                        "Converting all choices to strings."
+                    )
+                    choices = tuple(map(str, choices))
+                elif dtype not in (int, float, bool, str):
+                    _logger.warning(
+                        f"Choices are of type {dtype}, which is not supported by TensorBoard. "
+                        "Converting all choices to strings."
+                    )
+                    choices = tuple(map(str, choices))
+
                 self._hp_params[param_name] = hp.HParam(
                     param_name,
-                    hp.Discrete(param_distribution.choices),
+                    hp.Discrete(choices),
                 )
             else:
                 distribution_list = [

{"golden_diff": "diff --git a/optuna/integration/tensorboard.py b/optuna/integration/tensorboard.py\n--- a/optuna/integration/tensorboard.py\n+++ b/optuna/integration/tensorboard.py\n@@ -4,12 +4,15 @@\n import optuna\n from optuna._experimental import experimental_class\n from optuna._imports import try_import\n+from optuna.logging import get_logger\n \n \n with try_import() as _imports:\n     from tensorboard.plugins.hparams import api as hp\n     import tensorflow as tf\n \n+_logger = get_logger(__name__)\n+\n \n @experimental_class(\"2.0.0\")\n class TensorBoardCallback:\n@@ -46,7 +49,11 @@\n         for param_name, param_value in trial.params.items():\n             if param_name not in self._hp_params:\n                 self._add_distributions(trial.distributions)\n-            hparams[self._hp_params[param_name]] = param_value\n+            param = self._hp_params[param_name]\n+            if isinstance(param.domain, hp.Discrete):\n+                hparams[param] = param.domain.dtype(param_value)\n+            else:\n+                hparams[param] = param_value\n         run_name = \"trial-%d\" % trial.number\n         run_dir = os.path.join(self._dirname, run_name)\n         with tf.summary.create_file_writer(run_dir).as_default():\n@@ -74,9 +81,24 @@\n                     hp.IntInterval(param_distribution.low, param_distribution.high),\n                 )\n             elif isinstance(param_distribution, optuna.distributions.CategoricalDistribution):\n+                choices = param_distribution.choices\n+                dtype = type(choices[0])\n+                if any(not isinstance(choice, dtype) for choice in choices):\n+                    _logger.warning(\n+                        \"Choices contains mixed types, which is not supported by TensorBoard. \"\n+                        \"Converting all choices to strings.\"\n+                    )\n+                    choices = tuple(map(str, choices))\n+                elif dtype not in (int, float, bool, str):\n+                    _logger.warning(\n+                        f\"Choices are of type {dtype}, which is not supported by TensorBoard. \"\n+                        \"Converting all choices to strings.\"\n+                    )\n+                    choices = tuple(map(str, choices))\n+\n                 self._hp_params[param_name] = hp.HParam(\n                     param_name,\n-                    hp.Discrete(param_distribution.choices),\n+                    hp.Discrete(choices),\n                 )\n             else:\n                 distribution_list = [\n", "issue": "TensorBoard integration fails if None is suggested\n### Expected behavior\n\n`Optuna` with `TensorBoardCallback` should allow `None` as a suggestion.\n\n### Environment\n\n- Optuna version:3.2.0\r\n- Python version:3.9.13\r\n- OS:Linux-5.4.228-132.418.amzn2.x86_64-x86_64-with-glibc2.31\n\n### Error messages, stack traces, or logs\n\n```shell\nFile /opt/python3.9/site-packages/tensorboard/plugins/hparams/summary_v2.py:507, in Discrete.__init__(self, values, dtype)\r\n    505         raise ValueError(\"Empty domain with no dtype specified\")\r\n    506 if dtype not in (int, float, bool, str):\r\n--> 507     raise ValueError(\"Unknown dtype: %r\" % (dtype,))\r\n    508 self._dtype = dtype\r\n    509 for value in self._values:\r\n\r\nValueError: Unknown dtype: <class 'NoneType'>\r\n```\n```\n\n\n### Steps to reproduce\n\n1. If I run this\r\n```python\r\nimport optuna\r\nfrom optuna.integration.tensorboard import TensorBoardCallback\r\n\r\nfrom sklearn.ensemble import RandomForestClassifier\r\nfrom sklearn.datasets import make_classification\r\nfrom sklearn.model_selection import train_test_split\r\nfrom sklearn.metrics import f1_score\r\n\r\nX, y = make_classification(n_samples=1000, n_features=4,\r\n                           n_informative=2, n_redundant=0,\r\n                           random_state=0, shuffle=False)\r\n\r\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.33, random_state=0)\r\n\r\ndef objective(trial):\r\n    params = {\r\n        'max_leaf_nodes': trial.suggest_categorical('max_leaf_nodes', [None, 2, 5, 10, 20]),\r\n    }\r\n    clf = RandomForestClassifier()\r\n    clf.fit(X_train, y_train)\r\n\r\n    y_val_pred = clf.predict(X_val)\r\n    f1 = f1_score(y_val, y_val_pred)\r\n    return f1\r\n\r\ntensorboard_callback = TensorBoardCallback(\"logs/\", metric_name=\"f1\")\r\nstudy = optuna.create_study(direction=\"minimize\")\r\n\r\nstudy.optimize(objective, n_trials=10, timeout=600, callbacks=[tensorboard_callback])\r\n```\r\n\r\nIt would return an error above.\r\n\r\n2. If I don't have `None` in the suggestion or not use `TensorBoardCallback` then it would work fine.\n\n### Additional context (optional)\n\n_No response_\nTensorBoard integration fails if None is suggested\n### Expected behavior\n\n`Optuna` with `TensorBoardCallback` should allow `None` as a suggestion.\n\n### Environment\n\n- Optuna version:3.2.0\r\n- Python version:3.9.13\r\n- OS:Linux-5.4.228-132.418.amzn2.x86_64-x86_64-with-glibc2.31\n\n### Error messages, stack traces, or logs\n\n```shell\nFile /opt/python3.9/site-packages/tensorboard/plugins/hparams/summary_v2.py:507, in Discrete.__init__(self, values, dtype)\r\n    505         raise ValueError(\"Empty domain with no dtype specified\")\r\n    506 if dtype not in (int, float, bool, str):\r\n--> 507     raise ValueError(\"Unknown dtype: %r\" % (dtype,))\r\n    508 self._dtype = dtype\r\n    509 for value in self._values:\r\n\r\nValueError: Unknown dtype: <class 'NoneType'>\r\n```\n```\n\n\n### Steps to reproduce\n\n1. If I run this\r\n```python\r\nimport optuna\r\nfrom optuna.integration.tensorboard import TensorBoardCallback\r\n\r\nfrom sklearn.ensemble import RandomForestClassifier\r\nfrom sklearn.datasets import make_classification\r\nfrom sklearn.model_selection import train_test_split\r\nfrom sklearn.metrics import f1_score\r\n\r\nX, y = make_classification(n_samples=1000, n_features=4,\r\n                           n_informative=2, n_redundant=0,\r\n                           random_state=0, shuffle=False)\r\n\r\nX_train, X_val, y_train, y_val = train_test_split(X, y, test_size=0.33, random_state=0)\r\n\r\ndef objective(trial):\r\n    params = {\r\n        'max_leaf_nodes': trial.suggest_categorical('max_leaf_nodes', [None, 2, 5, 10, 20]),\r\n    }\r\n    clf = RandomForestClassifier()\r\n    clf.fit(X_train, y_train)\r\n\r\n    y_val_pred = clf.predict(X_val)\r\n    f1 = f1_score(y_val, y_val_pred)\r\n    return f1\r\n\r\ntensorboard_callback = TensorBoardCallback(\"logs/\", metric_name=\"f1\")\r\nstudy = optuna.create_study(direction=\"minimize\")\r\n\r\nstudy.optimize(objective, n_trials=10, timeout=600, callbacks=[tensorboard_callback])\r\n```\r\n\r\nIt would return an error above.\r\n\r\n2. If I don't have `None` in the suggestion or not use `TensorBoardCallback` then it would work fine.\n\n### Additional context (optional)\n\n_No response_\n", "before_files": [{"content": "import os\nfrom typing import Dict\n\nimport optuna\nfrom optuna._experimental import experimental_class\nfrom optuna._imports import try_import\n\n\nwith try_import() as _imports:\n    from tensorboard.plugins.hparams import api as hp\n    import tensorflow as tf\n\n\n@experimental_class(\"2.0.0\")\nclass TensorBoardCallback:\n    \"\"\"Callback to track Optuna trials with TensorBoard.\n\n    This callback adds relevant information that is tracked by Optuna to TensorBoard.\n\n    See `the example <https://github.com/optuna/optuna-examples/blob/main/\n    tensorboard/tensorboard_simple.py>`_.\n\n    Args:\n        dirname:\n            Directory to store TensorBoard logs.\n        metric_name:\n            Name of the metric. Since the metric itself is just a number,\n            `metric_name` can be used to give it a name. So you know later\n            if it was roc-auc or accuracy.\n\n    \"\"\"\n\n    def __init__(self, dirname: str, metric_name: str) -> None:\n        _imports.check()\n        self._dirname = dirname\n        self._metric_name = metric_name\n        self._hp_params: Dict[str, hp.HParam] = {}\n\n    def __call__(self, study: optuna.study.Study, trial: optuna.trial.FrozenTrial) -> None:\n        if len(self._hp_params) == 0:\n            self._initialization(study)\n        if trial.state != optuna.trial.TrialState.COMPLETE:\n            return\n        trial_value = trial.value if trial.value is not None else float(\"nan\")\n        hparams = {}\n        for param_name, param_value in trial.params.items():\n            if param_name not in self._hp_params:\n                self._add_distributions(trial.distributions)\n            hparams[self._hp_params[param_name]] = param_value\n        run_name = \"trial-%d\" % trial.number\n        run_dir = os.path.join(self._dirname, run_name)\n        with tf.summary.create_file_writer(run_dir).as_default():\n            hp.hparams(hparams, trial_id=run_name)  # record the values used in this trial\n            tf.summary.scalar(self._metric_name, trial_value, step=trial.number)\n\n    def _add_distributions(\n        self, distributions: Dict[str, optuna.distributions.BaseDistribution]\n    ) -> None:\n        supported_distributions = (\n            optuna.distributions.CategoricalDistribution,\n            optuna.distributions.FloatDistribution,\n            optuna.distributions.IntDistribution,\n        )\n\n        for param_name, param_distribution in distributions.items():\n            if isinstance(param_distribution, optuna.distributions.FloatDistribution):\n                self._hp_params[param_name] = hp.HParam(\n                    param_name,\n                    hp.RealInterval(float(param_distribution.low), float(param_distribution.high)),\n                )\n            elif isinstance(param_distribution, optuna.distributions.IntDistribution):\n                self._hp_params[param_name] = hp.HParam(\n                    param_name,\n                    hp.IntInterval(param_distribution.low, param_distribution.high),\n                )\n            elif isinstance(param_distribution, optuna.distributions.CategoricalDistribution):\n                self._hp_params[param_name] = hp.HParam(\n                    param_name,\n                    hp.Discrete(param_distribution.choices),\n                )\n            else:\n                distribution_list = [\n                    distribution.__name__ for distribution in supported_distributions\n                ]\n                raise NotImplementedError(\n                    \"The distribution {} is not implemented. \"\n                    \"The parameter distribution should be one of the {}\".format(\n                        param_distribution, distribution_list\n                    )\n                )\n\n    def _initialization(self, study: optuna.Study) -> None:\n        completed_trials = [\n            trial\n            for trial in study.get_trials(deepcopy=False)\n            if trial.state == optuna.trial.TrialState.COMPLETE\n        ]\n        for trial in completed_trials:\n            self._add_distributions(trial.distributions)\n", "path": "optuna/integration/tensorboard.py"}], "after_files": [{"content": "import os\nfrom typing import Dict\n\nimport optuna\nfrom optuna._experimental import experimental_class\nfrom optuna._imports import try_import\nfrom optuna.logging import get_logger\n\n\nwith try_import() as _imports:\n    from tensorboard.plugins.hparams import api as hp\n    import tensorflow as tf\n\n_logger = get_logger(__name__)\n\n\n@experimental_class(\"2.0.0\")\nclass TensorBoardCallback:\n    \"\"\"Callback to track Optuna trials with TensorBoard.\n\n    This callback adds relevant information that is tracked by Optuna to TensorBoard.\n\n    See `the example <https://github.com/optuna/optuna-examples/blob/main/\n    tensorboard/tensorboard_simple.py>`_.\n\n    Args:\n        dirname:\n            Directory to store TensorBoard logs.\n        metric_name:\n            Name of the metric. Since the metric itself is just a number,\n            `metric_name` can be used to give it a name. So you know later\n            if it was roc-auc or accuracy.\n\n    \"\"\"\n\n    def __init__(self, dirname: str, metric_name: str) -> None:\n        _imports.check()\n        self._dirname = dirname\n        self._metric_name = metric_name\n        self._hp_params: Dict[str, hp.HParam] = {}\n\n    def __call__(self, study: optuna.study.Study, trial: optuna.trial.FrozenTrial) -> None:\n        if len(self._hp_params) == 0:\n            self._initialization(study)\n        if trial.state != optuna.trial.TrialState.COMPLETE:\n            return\n        trial_value = trial.value if trial.value is not None else float(\"nan\")\n        hparams = {}\n        for param_name, param_value in trial.params.items():\n            if param_name not in self._hp_params:\n                self._add_distributions(trial.distributions)\n            param = self._hp_params[param_name]\n            if isinstance(param.domain, hp.Discrete):\n                hparams[param] = param.domain.dtype(param_value)\n            else:\n                hparams[param] = param_value\n        run_name = \"trial-%d\" % trial.number\n        run_dir = os.path.join(self._dirname, run_name)\n        with tf.summary.create_file_writer(run_dir).as_default():\n            hp.hparams(hparams, trial_id=run_name)  # record the values used in this trial\n            tf.summary.scalar(self._metric_name, trial_value, step=trial.number)\n\n    def _add_distributions(\n        self, distributions: Dict[str, optuna.distributions.BaseDistribution]\n    ) -> None:\n        supported_distributions = (\n            optuna.distributions.CategoricalDistribution,\n            optuna.distributions.FloatDistribution,\n            optuna.distributions.IntDistribution,\n        )\n\n        for param_name, param_distribution in distributions.items():\n            if isinstance(param_distribution, optuna.distributions.FloatDistribution):\n                self._hp_params[param_name] = hp.HParam(\n                    param_name,\n                    hp.RealInterval(float(param_distribution.low), float(param_distribution.high)),\n                )\n            elif isinstance(param_distribution, optuna.distributions.IntDistribution):\n                self._hp_params[param_name] = hp.HParam(\n                    param_name,\n                    hp.IntInterval(param_distribution.low, param_distribution.high),\n                )\n            elif isinstance(param_distribution, optuna.distributions.CategoricalDistribution):\n                choices = param_distribution.choices\n                dtype = type(choices[0])\n                if any(not isinstance(choice, dtype) for choice in choices):\n                    _logger.warning(\n                        \"Choices contains mixed types, which is not supported by TensorBoard. \"\n                        \"Converting all choices to strings.\"\n                    )\n                    choices = tuple(map(str, choices))\n                elif dtype not in (int, float, bool, str):\n                    _logger.warning(\n                        f\"Choices are of type {dtype}, which is not supported by TensorBoard. \"\n                        \"Converting all choices to strings.\"\n                    )\n                    choices = tuple(map(str, choices))\n\n                self._hp_params[param_name] = hp.HParam(\n                    param_name,\n                    hp.Discrete(choices),\n                )\n            else:\n                distribution_list = [\n                    distribution.__name__ for distribution in supported_distributions\n                ]\n                raise NotImplementedError(\n                    \"The distribution {} is not implemented. \"\n                    \"The parameter distribution should be one of the {}\".format(\n                        param_distribution, distribution_list\n                    )\n                )\n\n    def _initialization(self, study: optuna.Study) -> None:\n        completed_trials = [\n            trial\n            for trial in study.get_trials(deepcopy=False)\n            if trial.state == optuna.trial.TrialState.COMPLETE\n        ]\n        for trial in completed_trials:\n            self._add_distributions(trial.distributions)\n", "path": "optuna/integration/tensorboard.py"}]}