diff --git a/.coveragerc b/.coveragerc
new file mode 100644
index 00000000..26e44f99
--- /dev/null
+++ b/.coveragerc
@@ -0,0 +1,3 @@
+[run]
+concurrency = multiprocessing,thread
+source = optuna_dashboard/
diff --git a/.github/workflows/gh-pages.yml b/.github/workflows/gh-pages.yml
index c86b6929..311d606f 100644
--- a/.github/workflows/gh-pages.yml
+++ b/.github/workflows/gh-pages.yml
@@ -31,12 +31,15 @@ jobs:
with:
node-version: '18'
- name: Build standalone_app
+ env:
+ PUBLIC_PATH: /optuna-dashboard/public/
run: make MODE="prd" standalone_app/public/bundle.js
- name: Create publish directory
run: |
mkdir gh-publish
cp -r standalone_app/public gh-publish/public
- cp standalone_app/index.html gh-publish/index.html
+ cp standalone_app/favicon.ico gh-publish/favicon.ico
+ cp standalone_app/github-pages.html gh-publish/index.html
- name: Upload artifact
uses: actions/upload-pages-artifact@v2
with:
diff --git a/.github/workflows/python-coverage.yml b/.github/workflows/python-coverage.yml
new file mode 100644
index 00000000..4bda6350
--- /dev/null
+++ b/.github/workflows/python-coverage.yml
@@ -0,0 +1,48 @@
+name: python coverage
+
+on:
+ push:
+ branches:
+ - master
+ pull_request: {}
+
+jobs:
+ coverage:
+ runs-on: ubuntu-latest
+
+ # Not intended for forks.
+ if: github.repository == 'optuna/optuna-dashboard'
+
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+
+ - name: Setup Python
+ uses: actions/setup-python@v4
+ with:
+ python-version: '3.10'
+ architecture: x64
+
+ - name: Install dependencies
+ run: |
+ python -m pip install --progress-bar off --upgrade pip setuptools
+ pip install --progress-bar off .[optional]
+ pip install --progress-bar off .[test]
+ pip install --progress-bar off "optuna>=3.0.0"
+ pip install --progress-bar off .
+ echo 'import coverage; coverage.process_startup()' > sitecustomize.py
+ - name: Tests
+ env:
+ PYTHONPATH: . # To invoke sitecutomize.py
+ COVERAGE_PROCESS_START: .coveragerc # https://coverage.readthedocs.io/en/6.4.1/subprocess.html
+ COVERAGE_COVERAGE: yes # https://github.com/nedbat/coveragepy/blob/65bf33fc03209ffb01bbbc0d900017614645ee7a/coverage/control.py#L255-L261
+ run: |
+ coverage run --source=optuna_dashboard -m pytest python_tests
+ coverage combine
+ coverage xml
+ - name: Upload coverage to Codecov
+ uses: codecov/codecov-action@v3
+ with:
+ token: ${{ secrets.CODECOV_TOKEN }}
+ file: ./coverage.xml
+ fail_ci_if_error: true
diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml
index 612ddb59..8fee4e44 100644
--- a/.github/workflows/python-tests.yml
+++ b/.github/workflows/python-tests.yml
@@ -45,7 +45,8 @@ jobs:
# python_tests requires optuna>=3.0.0 since it imports FloatDistribution
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
- pip install streamlit boto3 moto[s3] pytest
+ pip install --progress-bar off .[optional]
+ pip install --progress-bar off .[test]
pip install --progress-bar off "optuna>=3.0.0"
pip install --progress-bar off .
- run: pytest python_tests
@@ -61,7 +62,8 @@ jobs:
- name: Install dependencies
run: |
python -m pip install --progress-bar off --upgrade pip setuptools
- pip install streamlit boto3 moto[s3] pytest
+ pip install --progress-bar off .[optional]
+ pip install --progress-bar off .[test]
pip install --progress-bar off .
python -m pip install --progress-bar off --upgrade git+https://github.com/optuna/optuna.git
- run: pytest python_tests
diff --git a/.gitignore b/.gitignore
index dde468fc..49c137e0 100644
--- a/.gitignore
+++ b/.gitignore
@@ -28,9 +28,16 @@ docs/_generated/
rustlib/target/
rustlib/pkg/
+# Test
+.coverage
+.coverage.*
+coverage.xml
+
# Others
.envrc
.idea/
.vscode/
.DS_Store
tmp/
+examples/preferential-optimization/artifact/
+
diff --git a/Makefile b/Makefile
index f925bb78..4094fd57 100644
--- a/Makefile
+++ b/Makefile
@@ -45,7 +45,7 @@ docs: docs/conf.py $(RST_FILES)
.PHONY: fmt
fmt:
npm run fmt
- black ./optuna_dashboard/ ./python_tests/
+ black ./optuna_dashboard/ ./python_tests/ ./e2e_tests/
isort .
.PHONY: clean
diff --git a/README.md b/README.md
index 4b38f863..62edd6c8 100644
--- a/README.md
+++ b/README.md
@@ -45,6 +45,8 @@ Listening on http://localhost:8080/
Hit Ctrl-C to quit.
```
+
+
Please check out [our documentation](https://optuna-dashboard.readthedocs.io/en/latest/getting-started.html) for more details.
## Using an official Docker image
@@ -75,27 +77,26 @@ $ docker run -it --rm -p 8080:8080 ghcr.io/optuna/optuna-dashboard postgresql+ps
+## Browser-only version (Experimental)
+
+
+
+We’ve developed the version that operates solely within your web browser, which internally uses SQLite3 Wasm and Rust.
+There’s no need to install Python or any other dependencies.
+Simply open the following URL in your browser, drag and drop your SQLite3 file onto the page, and you’re ready to view your Optuna studies!
+
+https://optuna.github.io/optuna-dashboard/
+
+*Please note that only a subset of features is available. However, you can still check the optimization history, hyperparameter importances, and etc. in graphs and tables.*
+
## VS Code Extension (Experimental)
You can install the VS Code extension via [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=Optuna.optuna-dashboard#overview).
-
+
Please right-click the SQLite3 files (`*.db` or `*.sqlite3`) in the VS Code file explorer and select the "Open in Optuna Dashboard" command from the dropdown menu.
-
-## Features
-
-### Manage Studies
-
-You can create and delete studies from Dashboard.
-
-
-
-### Visualize with Interactive Graphs & Rich Trials Data Grid
-
-You can check the optimization history, hyperparameter importances, etc. in graphs and tables.
-
-
+This extension leverages the browser-only version of Optuna Dashboard, so the same limitations apply.
## Submitting patches
diff --git a/docs/_static/optuna-dashboard.gif b/docs/_static/optuna-dashboard.gif
new file mode 100644
index 00000000..aea24d5a
Binary files /dev/null and b/docs/_static/optuna-dashboard.gif differ
diff --git a/docs/getting-started.rst b/docs/getting-started.rst
index a896fb36..295dd55c 100644
--- a/docs/getting-started.rst
+++ b/docs/getting-started.rst
@@ -1,6 +1,13 @@
Getting Started
===============
+.. figure:: _static/optuna-dashboard.gif
+ :alt: Optuna Dashboard
+ :align: center
+ :width: 800px
+
+ Optuna Dashboard
+
Installation
------------
diff --git a/e2e_tests/__init__.py b/e2e_tests/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/e2e_tests/conftest.py b/e2e_tests/conftest.py
index 8d8eac3b..ad1190cb 100644
--- a/e2e_tests/conftest.py
+++ b/e2e_tests/conftest.py
@@ -1,251 +1,6 @@
-import socket
-import threading
-from wsgiref.simple_server import make_server
-
-import optuna
-from optuna_dashboard import wsgi
import pytest
-study_names = [
- "single",
- "single-trial",
- "single-1-param",
- "single-dynamic",
- "single-inf",
- "multi-objective",
- "multi-dynamic",
- "single-pruned-without-report",
- "single-inf-report",
- "issue-410",
- "single-no-trials",
- "multi-no-trials",
-]
-
-
-def make_dummy_storage(study_name: str) -> optuna.storages.InMemoryStorage:
- storage = optuna.storages.InMemoryStorage()
- sampler = optuna.samplers.RandomSampler(seed=0)
-
- # Sinble objective study
- if study_name == "single":
- study = optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- def objective_single(trial: optuna.Trial) -> float:
- x1 = trial.suggest_float("x1", 0, 10)
- x2 = trial.suggest_float("x2", 0, 10)
- return (x1 - 2) ** 2 + (x2 - 5) ** 2
-
- study.optimize(objective_single, n_trials=50)
-
- # A single objective study with a single trial
- # Refs: https://github.com/optuna/optuna-dashboard/issues/401
- elif study_name == "single-trial":
- study = optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- def objective_single(trial: optuna.Trial) -> float:
- x1 = trial.suggest_float("x1", 0, 10)
- x2 = trial.suggest_float("x2", 0, 10)
- return (x1 - 2) ** 2 + (x2 - 5) ** 2
-
- study.optimize(objective_single, n_trials=1)
-
- # Single-objective study with 1 parameter
- elif study_name == "single-1-param":
- study = optuna.create_study(
- study_name=study_name, storage=storage, direction="maximize", sampler=sampler
- )
-
- def objective_single_with_1param(trial: optuna.Trial) -> float:
- x1 = trial.suggest_float("x1", 0, 10)
- return -((x1 - 2) ** 2)
-
- study.optimize(objective_single_with_1param, n_trials=50)
-
- # Single-objective study with dynamic search space
- elif study_name == "single-dynamic":
- study = optuna.create_study(
- study_name=study_name, storage=storage, direction="maximize", sampler=sampler
- )
-
- def objective_single_dynamic(trial: optuna.Trial) -> float:
- category = trial.suggest_categorical("category", ["foo", "bar"])
- if category == "foo":
- return (trial.suggest_float("x1", 0, 10) - 2) ** 2
- else:
- return -((trial.suggest_float("x2", -10, 0) + 5) ** 2)
-
- study.optimize(objective_single_dynamic, n_trials=50)
-
- # Single objective study with 'inf', '-inf', or 'nan' value
- elif study_name == "single-inf":
- study = optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- def objective_single_inf(trial: optuna.Trial) -> float:
- x = trial.suggest_float("x", -10, 10)
- if trial.number % 3 == 0:
- return float("inf")
- elif trial.number % 3 == 1:
- return float("-inf")
- else:
- return x**2
-
- study.optimize(objective_single_inf, n_trials=50)
-
- # Multi-objective study
- elif study_name == "multi-objective":
- study = optuna.create_study(
- study_name=study_name,
- storage=storage,
- directions=["minimize", "minimize"],
- sampler=sampler,
- )
-
- def objective_multi(trial: optuna.Trial) -> tuple[float, float]:
- x = trial.suggest_float("x", 0, 5)
- y = trial.suggest_float("y", 0, 3)
- v0 = 4 * x**2 + 4 * y**2
- v1 = (x - 5) ** 2 + (y - 5) ** 2
- return v0, v1
-
- study.optimize(objective_multi, n_trials=50)
-
- # Multi-objective study with dynamic search space
- elif study_name == "multi-dynamic":
- study = optuna.create_study(
- study_name=study_name,
- storage=storage,
- directions=["minimize", "minimize"],
- sampler=sampler,
- )
-
- def objective_multi_dynamic(trial: optuna.Trial) -> tuple[float, float]:
- category = trial.suggest_categorical("category", ["foo", "bar"])
- if category == "foo":
- x = trial.suggest_float("x1", 0, 5)
- y = trial.suggest_float("y1", 0, 3)
- v0 = 4 * x**2 + 4 * y**2
- v1 = (x - 5) ** 2 + (y - 5) ** 2
- return v0, v1
- else:
- x = trial.suggest_float("x2", 0, 5)
- y = trial.suggest_float("y2", 0, 3)
- v0 = 2 * x**2 + 2 * y**2
- v1 = (x - 2) ** 2 + (y - 3) ** 2
- return v0, v1
-
- study.optimize(objective_multi_dynamic, n_trials=50)
-
- # Pruning with no intermediate values
- elif study_name == "single-pruned-without-report":
- study = optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- def objective_prune_without_report(trial: optuna.Trial) -> float:
- x = trial.suggest_float("x", -15, 30)
- y = trial.suggest_float("y", -15, 30)
- v = x**2 + y**2
- if v > 100:
- raise optuna.TrialPruned()
- return v
-
- study.optimize(objective_prune_without_report, n_trials=100)
-
- # Single objective pruned after reported 'inf', '-inf', or 'nan'
- elif study_name == "single-inf-report":
- study = optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- def objective_single_inf_report(trial: optuna.Trial) -> float:
- x = trial.suggest_float("x", -10, 10)
- if trial.number % 3 == 0:
- trial.report(float("inf"), 1)
- elif trial.number % 3 == 1:
- trial.report(float("-inf"), 1)
- else:
- trial.report(float("nan"), 1)
-
- if x > 0:
- raise optuna.TrialPruned()
- else:
- return x**2
-
- study.optimize(objective_single_inf_report, n_trials=50)
-
- # Issue 410
- elif study_name == "issue-410":
- study = optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- def objective_issue_410(trial: optuna.Trial) -> float:
- trial.suggest_categorical("resample_rate", ["50ms"])
- trial.suggest_categorical("channels", ["all"])
- trial.suggest_categorical("window_size", [256])
- if trial.number > 15:
- raise Exception("Unexpected error")
- trial.suggest_categorical("cbow", [True])
- trial.suggest_categorical("model", ["m1"])
-
- trial.set_user_attr("epochs", 0)
- trial.set_user_attr("deterministic", True)
- if trial.number > 10:
- raise Exception("unexpeccted error")
- trial.set_user_attr("folder", "/path/to/folder")
- trial.set_user_attr("resample_type", "foo")
- trial.set_user_attr("run_id", "0001")
- return 1.0
-
- study.optimize(objective_issue_410, n_trials=20, catch=(Exception,))
-
- # No trials single-objective study
- elif study_name == "single-no-trials":
- optuna.create_study(study_name=study_name, storage=storage, sampler=sampler)
-
- # No trials multi-objective study
- elif study_name == "multi-no-trials":
- optuna.create_study(
- study_name=study_name,
- storage=storage,
- directions=["minimize", "maximize"],
- sampler=sampler,
- )
- else:
- assert False, f"No study configuration of {study_name} in conftest.py"
-
- return storage
-
-
-def get_free_port() -> int:
- tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
- tcp.bind(("", 0))
- _, port = tcp.getsockname()
- tcp.close()
- return port
-
-
-@pytest.fixture(scope="session", params=study_names)
-def storage(request: pytest.FixtureRequest) -> optuna.storages.InMemoryStorage:
- study_name = request.param
- storage = make_dummy_storage(study_name)
- return storage
-
-
-@pytest.fixture(scope="session")
-def server_url(request: pytest.FixtureRequest, storage: optuna.storages.InMemoryStorage) -> str:
- addr = "127.0.0.1"
- port = get_free_port()
- app = wsgi(storage)
- httpd = make_server(addr, port, app)
- thread = threading.Thread(target=httpd.serve_forever)
- thread.start()
-
- def stop_server() -> None:
- httpd.shutdown()
- httpd.server_close()
- thread.join()
-
- request.addfinalizer(stop_server)
-
- return f"http://{addr}:{port}/dashboard"
-
-
@pytest.fixture(scope="session")
def browser_context_args(browser_context_args: dict) -> dict:
return {
diff --git a/e2e_tests/test_server.py b/e2e_tests/test_server.py
new file mode 100644
index 00000000..b6d14734
--- /dev/null
+++ b/e2e_tests/test_server.py
@@ -0,0 +1,35 @@
+import socket
+import threading
+from wsgiref.simple_server import make_server
+
+import optuna
+from optuna_dashboard import wsgi
+import pytest
+
+
+def get_free_port() -> int:
+ tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
+ tcp.bind(("", 0))
+ _, port = tcp.getsockname()
+ tcp.close()
+ return port
+
+
+def make_test_server(
+ request: pytest.FixtureRequest, storage: optuna.storages.InMemoryStorage
+) -> str:
+ addr = "127.0.0.1"
+ port = get_free_port()
+ app = wsgi(storage)
+ httpd = make_server(addr, port, app)
+ thread = threading.Thread(target=httpd.serve_forever)
+ thread.start()
+
+ def stop_server() -> None:
+ httpd.shutdown()
+ httpd.server_close()
+ thread.join()
+
+ request.addfinalizer(stop_server)
+
+ return f"http://{addr}:{port}/dashboard"
diff --git a/e2e_tests/test_usecases/__init__.py b/e2e_tests/test_usecases/__init__.py
new file mode 100644
index 00000000..e69de29b
diff --git a/e2e_tests/test_usecases/test_study_history.py b/e2e_tests/test_usecases/test_study_history.py
index f34d35e6..e37b8f5a 100644
--- a/e2e_tests/test_usecases/test_study_history.py
+++ b/e2e_tests/test_usecases/test_study_history.py
@@ -1,5 +1,35 @@
import optuna
from playwright.sync_api import Page
+import pytest
+
+from ..test_server import make_test_server
+
+
+def make_test_storage() -> optuna.storages.InMemoryStorage:
+ storage = optuna.storages.InMemoryStorage()
+ sampler = optuna.samplers.RandomSampler(seed=0)
+
+ study = optuna.create_study(study_name="single", storage=storage, sampler=sampler)
+
+ def objective_single(trial: optuna.Trial) -> float:
+ x1 = trial.suggest_float("x1", 0, 10)
+ x2 = trial.suggest_float("x2", 0, 10)
+ return (x1 - 2) ** 2 + (x2 - 5) ** 2
+
+ study.optimize(objective_single, n_trials=50)
+
+ return storage
+
+
+@pytest.fixture
+def storage() -> optuna.storages.InMemoryStorage:
+ storage = make_test_storage()
+ return storage
+
+
+@pytest.fixture
+def server_url(request: pytest.FixtureRequest, storage: optuna.storages.InMemoryStorage) -> str:
+ return make_test_server(request, storage)
def test_history_xaxis_click(
diff --git a/e2e_tests/visual_regression_test.py b/e2e_tests/visual_regression_test.py
index 706ec2f3..d83fbb5f 100644
--- a/e2e_tests/visual_regression_test.py
+++ b/e2e_tests/visual_regression_test.py
@@ -1,15 +1,274 @@
+from typing import Callable
+
import optuna
from playwright.sync_api import Page
+import pytest
+
+from .test_server import make_test_server
+@pytest.fixture
+def storage() -> optuna.storages.InMemoryStorage:
+ storage = optuna.storages.InMemoryStorage()
+ return storage
+
+
+@pytest.fixture
+def server_url(request: pytest.FixtureRequest, storage: optuna.storages.InMemoryStorage) -> str:
+ return make_test_server(request, storage)
+
+
+def run_single_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(study_name="single", storage=storage, sampler=sampler)
+
+ def objective(trial: optuna.Trial) -> float:
+ x1 = trial.suggest_float("x1", 0, 10)
+ x2 = trial.suggest_float("x2", 0, 10)
+ return (x1 - 2) ** 2 + (x2 - 5) ** 2
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_single_trial_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # A single objective study with a single trial
+ # Refs: https://github.com/optuna/optuna-dashboard/issues/401
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(study_name="single-trial", storage=storage, sampler=sampler)
+
+ def objective(trial: optuna.Trial) -> float:
+ x1 = trial.suggest_float("x1", 0, 10)
+ x2 = trial.suggest_float("x2", 0, 10)
+ return (x1 - 2) ** 2 + (x2 - 5) ** 2
+
+ study.optimize(objective, n_trials=1)
+ return study
+
+
+def run_single_1param_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(
+ study_name="single-1-param", storage=storage, direction="maximize", sampler=sampler
+ )
+
+ def objective(trial: optuna.Trial) -> float:
+ x1 = trial.suggest_float("x1", 0, 10)
+ return -((x1 - 2) ** 2)
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_single_dynamic_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # Single-objective study with dynamic search space
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(
+ study_name="single-dynamic", storage=storage, direction="maximize", sampler=sampler
+ )
+
+ def objective(trial: optuna.Trial) -> float:
+ category = trial.suggest_categorical("category", ["foo", "bar"])
+ if category == "foo":
+ return (trial.suggest_float("x1", 0, 10) - 2) ** 2
+ else:
+ return -((trial.suggest_float("x2", -10, 0) + 5) ** 2)
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_single_inf_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # Single objective study with 'inf', '-inf', or 'nan' value
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(study_name="single-inf", storage=storage, sampler=sampler)
+
+ def objective(trial: optuna.Trial) -> float:
+ x = trial.suggest_float("x", -10, 10)
+ if trial.number % 3 == 0:
+ return float("inf")
+ elif trial.number % 3 == 1:
+ return float("-inf")
+ else:
+ return x**2
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_multi_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # Multi-objective study
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(
+ study_name="multi-objective",
+ storage=storage,
+ directions=["minimize", "minimize"],
+ sampler=sampler,
+ )
+
+ def objective(trial: optuna.Trial) -> tuple[float, float]:
+ x = trial.suggest_float("x", 0, 5)
+ y = trial.suggest_float("y", 0, 3)
+ v0 = 4 * x**2 + 4 * y**2
+ v1 = (x - 5) ** 2 + (y - 5) ** 2
+ return v0, v1
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_multi_dynamic_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # Multi-objective study with dynamic search space
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(
+ study_name="multi-dynamic",
+ storage=storage,
+ directions=["minimize", "minimize"],
+ sampler=sampler,
+ )
+
+ def objective(trial: optuna.Trial) -> tuple[float, float]:
+ category = trial.suggest_categorical("category", ["foo", "bar"])
+ if category == "foo":
+ x = trial.suggest_float("x1", 0, 5)
+ y = trial.suggest_float("y1", 0, 3)
+ v0 = 4 * x**2 + 4 * y**2
+ v1 = (x - 5) ** 2 + (y - 5) ** 2
+ return v0, v1
+ else:
+ x = trial.suggest_float("x2", 0, 5)
+ y = trial.suggest_float("y2", 0, 3)
+ v0 = 2 * x**2 + 2 * y**2
+ v1 = (x - 2) ** 2 + (y - 3) ** 2
+ return v0, v1
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_single_pruned_without_report_objective_study(
+ storage: optuna.storages.InMemoryStorage,
+) -> optuna.Study:
+ # Pruning with no intermediate values
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(
+ study_name="single-pruned-without-report", storage=storage, sampler=sampler
+ )
+
+ def objective(trial: optuna.Trial) -> float:
+ x = trial.suggest_float("x", -15, 30)
+ y = trial.suggest_float("y", -15, 30)
+ v = x**2 + y**2
+ if v > 100:
+ raise optuna.TrialPruned()
+ return v
+
+ study.optimize(objective, n_trials=100)
+ return study
+
+
+def run_single_inf_report_objective_study(
+ storage: optuna.storages.InMemoryStorage,
+) -> optuna.Study:
+ # Single objective pruned after reported 'inf', '-inf', or 'nan'
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(study_name="single-inf-report", storage=storage, sampler=sampler)
+
+ def objective(trial: optuna.Trial) -> float:
+ x = trial.suggest_float("x", -10, 10)
+ if trial.number % 3 == 0:
+ trial.report(float("inf"), 1)
+ elif trial.number % 3 == 1:
+ trial.report(float("-inf"), 1)
+ else:
+ trial.report(float("nan"), 1)
+
+ if x > 0:
+ raise optuna.TrialPruned()
+ else:
+ return x**2
+
+ study.optimize(objective, n_trials=50)
+ return study
+
+
+def run_issue_410_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # Issue 410
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(study_name="issue-410", storage=storage, sampler=sampler)
+
+ def objective(trial: optuna.Trial) -> float:
+ trial.suggest_categorical("resample_rate", ["50ms"])
+ trial.suggest_categorical("channels", ["all"])
+ trial.suggest_categorical("window_size", [256])
+ if trial.number > 15:
+ raise Exception("Unexpected error")
+ trial.suggest_categorical("cbow", [True])
+ trial.suggest_categorical("model", ["m1"])
+
+ trial.set_user_attr("epochs", 0)
+ trial.set_user_attr("deterministic", True)
+ if trial.number > 10:
+ raise Exception("unexpeccted error")
+ trial.set_user_attr("folder", "/path/to/folder")
+ trial.set_user_attr("resample_type", "foo")
+ trial.set_user_attr("run_id", "0001")
+ return 1.0
+
+ study.optimize(objective, n_trials=20, catch=(Exception,))
+ return study
+
+
+def run_single_no_trials_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # No trials single-objective study
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(study_name="single-no-trials", storage=storage, sampler=sampler)
+
+ return study
+
+
+def run_multi_no_trials_objective_study(storage: optuna.storages.InMemoryStorage) -> optuna.Study:
+ # No trials multi-objective study
+ sampler = optuna.samplers.RandomSampler(seed=0)
+ study = optuna.create_study(
+ study_name="multi-no-trials",
+ storage=storage,
+ directions=["minimize", "maximize"],
+ sampler=sampler,
+ )
+ return study
+
+
+parameterize_studies = pytest.mark.parametrize(
+ "run_study",
+ [
+ run_single_objective_study,
+ run_single_trial_objective_study,
+ run_single_1param_objective_study,
+ run_single_dynamic_objective_study,
+ run_single_inf_objective_study,
+ run_multi_objective_study,
+ run_multi_dynamic_objective_study,
+ run_single_pruned_without_report_objective_study,
+ run_single_inf_report_objective_study,
+ run_issue_410_objective_study,
+ run_single_no_trials_objective_study,
+ run_multi_no_trials_objective_study,
+ ],
+)
+
+
+@parameterize_studies
def test_study_list(
page: Page,
storage: optuna.storages.InMemoryStorage,
server_url: str,
+ run_study: Callable[[optuna.storages.InMemoryStorage], optuna.Study],
) -> None:
- summaries = optuna.get_all_study_summaries(storage)
- study_id = summaries[0]._study_id
- study_name = summaries[0].study_name
+ study = run_study(storage)
+
+ study_id = study._study_id
+ study_name = study.study_name
page.goto(server_url)
page.click(f"a[href='/dashboard/studies/{study_id}']")
@@ -22,14 +281,17 @@ def test_study_list(
assert study_name in title
+@parameterize_studies
def test_study_analytics(
page: Page,
storage: optuna.storages.InMemoryStorage,
server_url: str,
+ run_study: Callable[[optuna.storages.InMemoryStorage], optuna.Study],
) -> None:
- summaries = optuna.get_all_study_summaries(storage)
- study_id = summaries[0]._study_id
- study_name = summaries[0].study_name
+ study = run_study(storage)
+
+ study_id = study._study_id
+ study_name = study.study_name
url = f"{server_url}/studies/{study_id}"
page.goto(url)
@@ -43,14 +305,17 @@ def test_study_analytics(
assert study_name in title
+@parameterize_studies
def test_trial_list(
page: Page,
storage: optuna.storages.InMemoryStorage,
server_url: str,
+ run_study: Callable[[optuna.storages.InMemoryStorage], optuna.Study],
) -> None:
- summaries = optuna.get_all_study_summaries(storage)
- study_id = summaries[0]._study_id
- study_name = summaries[0].study_name
+ study = run_study(storage)
+
+ study_id = study._study_id
+ study_name = study.study_name
url = f"{server_url}/studies/{study_id}"
page.goto(url)
@@ -64,14 +329,17 @@ def test_trial_list(
assert study_name in title
+@parameterize_studies
def test_trial_table(
page: Page,
storage: optuna.storages.InMemoryStorage,
server_url: str,
+ run_study: Callable[[optuna.storages.InMemoryStorage], optuna.Study],
) -> None:
- summaries = optuna.get_all_study_summaries(storage)
- study_id = summaries[0]._study_id
- study_name = summaries[0].study_name
+ study = run_study(storage)
+
+ study_id = study._study_id
+ study_name = study.study_name
url = f"{server_url}/studies/{study_id}"
page.goto(url)
@@ -85,14 +353,17 @@ def test_trial_table(
assert study_name in title
+@parameterize_studies
def test_trial_note(
page: Page,
storage: optuna.storages.InMemoryStorage,
server_url: str,
+ run_study: Callable[[optuna.storages.InMemoryStorage], optuna.Study],
) -> None:
- summaries = optuna.get_all_study_summaries(storage)
- study_id = summaries[0]._study_id
- study_name = summaries[0].study_name
+ study = run_study(storage)
+
+ study_id = study._study_id
+ study_name = study.study_name
url = f"{server_url}/studies/{study_id}"
page.goto(url)
diff --git a/examples/preferential-optimization/generator.py b/examples/preferential-optimization/generator.py
index cfbbade2..e94f1d05 100644
--- a/examples/preferential-optimization/generator.py
+++ b/examples/preferential-optimization/generator.py
@@ -20,11 +20,10 @@ artifact_path = os.path.join(os.path.dirname(__file__), "artifact")
artifact_backend = FileSystemBackend(base_path=artifact_path)
os.makedirs(artifact_path, exist_ok=True)
-n_comparison = 5
-
def main() -> NoReturn:
study = create_study(
+ n_generate=5,
study_name="Preferential Optimization",
storage=STORAGE_URL,
sampler=PreferentialGPSampler(),
@@ -35,7 +34,7 @@ def main() -> NoReturn:
while True:
# If n_comparison "best" trials (that are not reported bad) exists,
# the generator waits for human evaluation.
- if len(study.best_trials) >= n_comparison:
+ if not study.should_generate():
time.sleep(0.1) # Avoid busy-loop
continue
diff --git a/optuna_dashboard/preferential/_study.py b/optuna_dashboard/preferential/_study.py
index fba9b4cd..ce691f42 100644
--- a/optuna_dashboard/preferential/_study.py
+++ b/optuna_dashboard/preferential/_study.py
@@ -12,9 +12,11 @@ from optuna.samplers import BaseSampler
from optuna.samplers import RandomSampler
from optuna.trial import FrozenTrial
from optuna.trial import TrialState
+from optuna_dashboard.preferential._system_attrs import get_n_generate
from optuna_dashboard.preferential._system_attrs import get_preferences
from optuna_dashboard.preferential._system_attrs import is_skipped_trial
from optuna_dashboard.preferential._system_attrs import report_preferences
+from optuna_dashboard.preferential._system_attrs import set_n_generate
_logger = logging.get_logger(__name__)
@@ -253,6 +255,16 @@ class PreferentialStudy:
raise RuntimeError("Unexpected trial type")
storage.set_trial_system_attr(trial_id, _SYSTEM_ATTR_COMPARISON_READY, True)
+ def should_generate(self) -> bool:
+ """Return whether the generator should generate a new trial now.
+
+ Returns :obj:`True` if the number of trials not reported bad and not skipped are less than
+ :attr:`~optuna_dashboard.preferential.PreferentialStudy.n_generate`. Users are recommended
+ to generate a new trial if this method returns :obj:`True`, and to wait for human
+ evaluation if this method returns :obj:`False`.
+ """
+ return len(self.best_trials) < get_n_generate(self._study.system_attrs)
+
def get_best_trials(study_id: int, storage: optuna.storages.BaseStorage) -> list[FrozenTrial]:
preferences = get_preferences(study_id, storage)
@@ -274,6 +286,7 @@ def get_best_trials(study_id: int, storage: optuna.storages.BaseStorage) -> list
def create_study(
*,
+ n_generate: int,
storage: str | optuna.storages.BaseStorage | None = None,
sampler: BaseSampler | None = None,
study_name: str | None = None,
@@ -293,6 +306,12 @@ def create_study(
trial = study.ask()
Args:
+ n_generate:
+ The number of active trials to keep.
+ :func:`~optuna_dashboard.preferential.PreferentialStudy.should_generate` returns
+ :obj:`True` if the number of trials not reported bad and not skipped are less than
+ ``n_generate``.
+
storage:
Database URL. If this argument is set to None, in-memory storage is used, and the
:class:`~optuna_dashboard.preferential.PreferentialStudy` will not be persistent.
@@ -328,6 +347,7 @@ def create_study(
study._storage.set_study_system_attr(
study._study_id, _SYSTEM_ATTR_PREFERENTIAL_STUDY, True
)
+ set_n_generate(study._study_id, study._storage, n_generate)
return PreferentialStudy(study)
except optuna.exceptions.DuplicatedStudyError:
diff --git a/optuna_dashboard/preferential/_system_attrs.py b/optuna_dashboard/preferential/_system_attrs.py
index 8c3de407..2964c9e0 100644
--- a/optuna_dashboard/preferential/_system_attrs.py
+++ b/optuna_dashboard/preferential/_system_attrs.py
@@ -9,6 +9,7 @@ from optuna.trial import TrialState
_SYSTEM_ATTR_PREFIX_PREFERENCE = "preference:values"
_SYSTEM_ATTR_PREFIX_SKIP_TRIAL = "preference:skip_trial:"
+_SYSTEM_ATTR_N_GENERATE = "preference:n_generate"
def report_preferences(
@@ -60,3 +61,15 @@ def report_skip(
def is_skipped_trial(trial_id: int, study_system_attrs: dict[str, Any]) -> bool:
key = _SYSTEM_ATTR_PREFIX_SKIP_TRIAL + str(trial_id)
return key in study_system_attrs
+
+
+def get_n_generate(study_system_attrs: dict[str, Any]) -> int:
+ return study_system_attrs[_SYSTEM_ATTR_N_GENERATE]
+
+
+def set_n_generate(study_id: int, storage: BaseStorage, n_generate: int) -> None:
+ storage.set_study_system_attr(
+ study_id=study_id,
+ key=_SYSTEM_ATTR_N_GENERATE,
+ value=n_generate,
+ )
diff --git a/optuna_dashboard/ts/components/AppDrawer.tsx b/optuna_dashboard/ts/components/AppDrawer.tsx
index 79f7d615..0903d72b 100644
--- a/optuna_dashboard/ts/components/AppDrawer.tsx
+++ b/optuna_dashboard/ts/components/AppDrawer.tsx
@@ -204,21 +204,19 @@ export const AppDrawer: FC<{
/>
- {!isPreferential && (
-
-
-
-
-
-
-
-
- )}
+
+
+
+
+
+
+
+
Trial {trial.number}
}
/>
-
- Objective Values = [{trial.values?.join(", ")}]
-
+ {studyDetail?.is_preferential ? null : (
+
+ Objective Values = [{trial.values?.join(", ")}]
+
+ )}
Params = [
{trial.params
diff --git a/optuna_dashboard/ts/components/GraphContour.tsx b/optuna_dashboard/ts/components/GraphContour.tsx
index d7e2aa13..c75e1500 100644
--- a/optuna_dashboard/ts/components/GraphContour.tsx
+++ b/optuna_dashboard/ts/components/GraphContour.tsx
@@ -11,6 +11,7 @@ import {
useTheme,
Box,
} from "@mui/material"
+import blue from "@mui/material/colors/blue"
import { plotlyDarkTemplate } from "./PlotlyDarkMode"
import { useMergedUnionSearchSpace } from "../searchSpace"
@@ -211,32 +212,62 @@ const plotContour = (
}
})
+ if (!study.is_preferential) {
+ const plotData: Partial[] = [
+ {
+ type: "contour",
+ x: xIndices,
+ y: yIndices,
+ z: zValues,
+ colorscale: "Blues",
+ connectgaps: true,
+ hoverinfo: "none",
+ line: {
+ smoothing: 1.3,
+ },
+ reversescale: study.directions[objectiveId] !== "minimize",
+ // https://github.com/plotly/react-plotly.js/issues/251
+ // @ts-ignore
+ contours: {
+ coloring: "heatmap",
+ },
+ },
+ {
+ type: "scatter",
+ x: xValues,
+ y: yValues,
+ marker: { line: { width: 2.0, color: "Grey" }, color: "black" },
+ mode: "markers",
+ showlegend: false,
+ },
+ ]
+ plotly.react(plotDomId, plotData, layout)
+ return
+ }
+
+ layout.legend = {
+ y: 0.8,
+ }
+ const bestTrialIndices = study.best_trials.map((trial) => trial.number)
const plotData: Partial[] = [
{
- type: "contour",
- x: xIndices,
- y: yIndices,
- z: zValues,
- colorscale: "Blues",
- connectgaps: true,
- hoverinfo: "none",
- line: {
- smoothing: 1.3,
- },
- reversescale: study.directions[objectiveId] !== "minimize",
- // https://github.com/plotly/react-plotly.js/issues/251
- // @ts-ignore
- contours: {
- coloring: "heatmap",
+ type: "scatter",
+ x: xValues.filter((_, i) => bestTrialIndices.includes(i)),
+ y: yValues.filter((_, i) => bestTrialIndices.includes(i)),
+ marker: {
+ line: { width: 2.0, color: "Grey" },
+ color: blue[200],
},
+ name: "best trials",
+ mode: "markers",
},
{
type: "scatter",
- x: xValues,
- y: yValues,
+ x: xValues.filter((_, i) => !bestTrialIndices.includes(i)),
+ y: yValues.filter((_, i) => !bestTrialIndices.includes(i)),
marker: { line: { width: 2.0, color: "Grey" }, color: "black" },
+ name: "others",
mode: "markers",
- showlegend: false,
},
]
plotly.react(plotDomId, plotData, layout)
diff --git a/optuna_dashboard/ts/components/PreferentialAnalytics.tsx b/optuna_dashboard/ts/components/PreferentialAnalytics.tsx
new file mode 100644
index 00000000..d8623f97
--- /dev/null
+++ b/optuna_dashboard/ts/components/PreferentialAnalytics.tsx
@@ -0,0 +1,68 @@
+import React, { FC } from "react"
+import {
+ Box,
+ Card,
+ CardContent,
+ Paper,
+ Typography,
+ useTheme,
+} from "@mui/material"
+import Grid2 from "@mui/material/Unstable_Grid2"
+import { DataGrid, DataGridColumn } from "./DataGrid"
+import { BestTrialsCard } from "./BestTrialsCard"
+import { useStudyDetailValue, useStudySummaryValue } from "../state"
+import { Contour } from "./GraphContour"
+
+export const PreferentialAnalytics: FC<{ studyId: number }> = ({ studyId }) => {
+ const theme = useTheme()
+ const studySummary = useStudySummaryValue(studyId)
+ const studyDetail = useStudyDetailValue(studyId)
+
+ const userAttrs = studySummary?.user_attrs || studyDetail?.user_attrs || []
+ const userAttrColumns: DataGridColumn[] = [
+ { field: "key", label: "Key", sortable: true },
+ { field: "value", label: "Value", sortable: true },
+ ]
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Study User Attributes
+
+
+ columns={userAttrColumns}
+ rows={userAttrs}
+ keyField={"key"}
+ dense={true}
+ initialRowsPerPage={5}
+ rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
+ />
+
+
+
+
+
+ )
+}
diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx
index 738166b5..fedfd625 100644
--- a/optuna_dashboard/ts/components/StudyDetail.tsx
+++ b/optuna_dashboard/ts/components/StudyDetail.tsx
@@ -30,6 +30,7 @@ import { GraphEdf } from "./GraphEdf"
import { TrialList } from "./TrialList"
import { StudyHistory } from "./StudyHistory"
import { PreferentialTrials } from "./PreferentialTrials"
+import { PreferentialAnalytics } from "./PreferentialAnalytics"
interface ParamTypes {
studyId: string
@@ -98,7 +99,9 @@ export const StudyDetail: FC<{
)
} else if (page === "analytics") {
- content = (
+ content = isPreferential ? (
+
+ ) : (
Hyperparameter Relationships
diff --git a/pyproject.toml b/pyproject.toml
index d9c0082c..0555f701 100644
--- a/pyproject.toml
+++ b/pyproject.toml
@@ -41,6 +41,18 @@ docs = [
"sphinx_rtd_theme",
]
+test = [
+ "coverage",
+ "pytest",
+ "moto[s3]",
+]
+
+optional = [
+ "streamlit",
+ "boto3",
+]
+
+
[project.scripts]
optuna-dashboard = "optuna_dashboard._cli:main"
diff --git a/python_tests/preferential/test_study.py b/python_tests/preferential/test_study.py
index 3e0b011c..3de57dae 100644
--- a/python_tests/preferential/test_study.py
+++ b/python_tests/preferential/test_study.py
@@ -25,7 +25,7 @@ from ..storage_supplier import StorageSupplier
@parametrize_storages
def test_study_set_and_get_user_attrs(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
study.set_user_attr("dataset", "MNIST")
assert study.user_attrs["dataset"] == "MNIST"
@@ -34,7 +34,7 @@ def test_study_set_and_get_user_attrs(storage_supplier: Callable[[], StorageSupp
@parametrize_storages
def test_report_and_get_preferences(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
assert len(study.preferences) == 0
for _ in range(2):
@@ -51,7 +51,9 @@ def test_report_and_get_preferences(storage_supplier: Callable[[], StorageSuppli
def test_study_pickle() -> None:
- study_1 = create_study()
+ study_1 = create_study(
+ n_generate=4,
+ )
for _ in range(10):
study_1.ask()
assert len(study_1.trials) == 10
@@ -69,13 +71,17 @@ def test_study_pickle() -> None:
def test_create_study(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
# Test creating a new study.
- study = create_study(storage=storage, load_if_exists=False)
+ study = create_study(n_generate=4, storage=storage, load_if_exists=False)
# Test `load_if_exists=True` with existing study.
- create_study(study_name=study.study_name, storage=storage, load_if_exists=True)
+ create_study(
+ n_generate=4, study_name=study.study_name, storage=storage, load_if_exists=True
+ )
with pytest.raises(DuplicatedStudyError):
- create_study(study_name=study.study_name, storage=storage, load_if_exists=False)
+ create_study(
+ n_generate=4, study_name=study.study_name, storage=storage, load_if_exists=False
+ )
@parametrize_storages
@@ -92,7 +98,7 @@ def test_load_study(storage_supplier: Callable[[], StorageSupplier]) -> None:
load_study(study_name=study_name, storage=storage)
# Create a new study.
- created_study = create_study(study_name=study_name, storage=storage)
+ created_study = create_study(n_generate=4, study_name=study_name, storage=storage)
# Test loading an existing study.
loaded_study = load_study(study_name=study_name, storage=storage)
@@ -108,7 +114,7 @@ def test_load_study_study_name_none(storage_supplier: Callable[[], StorageSuppli
study_name = str(uuid.uuid4())
- _ = create_study(study_name=study_name, storage=storage)
+ _ = create_study(n_generate=4, study_name=study_name, storage=storage)
loaded_study = load_study(study_name=None, storage=storage)
@@ -116,7 +122,7 @@ def test_load_study_study_name_none(storage_supplier: Callable[[], StorageSuppli
study_name = str(uuid.uuid4())
- _ = create_study(study_name=study_name, storage=storage)
+ _ = create_study(n_generate=4, study_name=study_name, storage=storage)
# Ambiguous study.
with pytest.raises(ValueError):
@@ -131,7 +137,7 @@ def test_delete_study(storage_supplier: Callable[[], StorageSupplier]) -> None:
delete_study(study_name="invalid-study-name", storage=storage)
# Test deleting an existing study.
- study = create_study(storage=storage, load_if_exists=False)
+ study = create_study(n_generate=4, storage=storage, load_if_exists=False)
delete_study(study_name=study.study_name, storage=storage)
# Test failed to delete the study which is already deleted.
@@ -141,7 +147,7 @@ def test_delete_study(storage_supplier: Callable[[], StorageSupplier]) -> None:
def test_copy_study() -> None:
with StorageSupplier("sqlite") as from_storage, StorageSupplier("sqlite") as to_storage:
- from_study = create_study(storage=from_storage)
+ from_study = create_study(n_generate=4, storage=from_storage)
from_study.set_user_attr("baz", "qux")
for _ in range(3):
trial = from_study.ask()
@@ -165,8 +171,8 @@ def test_copy_study() -> None:
def test_copy_study_to_study_name() -> None:
with StorageSupplier("sqlite") as from_storage, StorageSupplier("sqlite") as to_storage:
- from_study = create_study(study_name="foo", storage=from_storage)
- _ = create_study(study_name="foo", storage=to_storage)
+ from_study = create_study(n_generate=4, study_name="foo", storage=from_storage)
+ _ = create_study(n_generate=4, study_name="foo", storage=to_storage)
with pytest.raises(DuplicatedStudyError):
copy_study(
@@ -188,7 +194,7 @@ def test_copy_study_to_study_name() -> None:
@parametrize_storages
def test_add_trial(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
assert len(study.trials) == 0
trial = create_trial(value=0)
@@ -198,7 +204,9 @@ def test_add_trial(storage_supplier: Callable[[], StorageSupplier]) -> None:
def test_add_trial_invalid_values_length() -> None:
- study = create_study()
+ study = create_study(
+ n_generate=4,
+ )
trial = create_trial(values=[0, 0])
with pytest.raises(ValueError):
study.add_trial(trial)
@@ -207,7 +215,7 @@ def test_add_trial_invalid_values_length() -> None:
@parametrize_storages
def test_add_trials(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
assert len(study.trials) == 0
study.add_trials([])
@@ -220,7 +228,7 @@ def test_add_trials(storage_supplier: Callable[[], StorageSupplier]) -> None:
assert trial.number == i
assert trial.value == i
- other_study = create_study(storage=storage)
+ other_study = create_study(n_generate=4, storage=storage)
other_study.add_trials(study.trials)
assert len(other_study.trials) == 3
for i, trial in enumerate(other_study.trials):
@@ -231,7 +239,7 @@ def test_add_trials(storage_supplier: Callable[[], StorageSupplier]) -> None:
@parametrize_storages
def test_get_trials(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
for _ in range(5):
trial = study.ask()
trial.suggest_int("x", 1, 5)
@@ -256,7 +264,7 @@ def test_get_trials(storage_supplier: Callable[[], StorageSupplier]) -> None:
@parametrize_storages
def test_get_trials_state_option(storage_supplier: Callable[[], StorageSupplier]) -> None:
with storage_supplier() as storage:
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
for _ in range(3):
trial = study.ask()
study.mark_comparison_ready(trial)
@@ -286,7 +294,9 @@ def test_get_trials_state_option(storage_supplier: Callable[[], StorageSupplier]
def test_ask() -> None:
- study = create_study()
+ study = create_study(
+ n_generate=4,
+ )
trial = study.ask()
assert isinstance(trial, Trial)
@@ -298,7 +308,9 @@ def test_ask_fixed_search_space() -> None:
"y": distributions.CategoricalDistribution(["bacon", "spam"]),
}
- study = create_study()
+ study = create_study(
+ n_generate=4,
+ )
trial = study.ask(fixed_distributions=fixed_distributions)
params = trial.params
@@ -312,7 +324,7 @@ def test_report_preferences_from_another_process() -> None:
with StorageSupplier("sqlite") as storage:
# Create a study and ask for a new trial.
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
study.ask()
study.ask()
diff --git a/python_tests/test_api.py b/python_tests/test_api.py
index 51017ac8..7633cd8f 100644
--- a/python_tests/test_api.py
+++ b/python_tests/test_api.py
@@ -103,7 +103,7 @@ class APITestCase(TestCase):
def test_get_best_trials_of_preferential_study(self) -> None:
storage = optuna.storages.InMemoryStorage()
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
for _ in range(3):
trial = study.ask()
study.mark_comparison_ready(trial)
@@ -126,7 +126,7 @@ class APITestCase(TestCase):
def test_report_preference(self) -> None:
storage = optuna.storages.InMemoryStorage()
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
for _ in range(3):
trial = study.ask()
study.mark_comparison_ready(trial)
@@ -216,7 +216,7 @@ class APITestCase(TestCase):
def test_skip_trial(self) -> None:
storage = optuna.storages.InMemoryStorage()
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
trials: list[optuna.Trial] = []
for _ in range(3):
trial = study.ask()
diff --git a/python_tests/test_serializers.py b/python_tests/test_serializers.py
index a75db32d..a90e0de7 100644
--- a/python_tests/test_serializers.py
+++ b/python_tests/test_serializers.py
@@ -24,7 +24,7 @@ def test_serialize_dict() -> None:
def test_get_study_detail_is_preferential() -> None:
storage = optuna.storages.InMemoryStorage()
- study = create_study(storage=storage)
+ study = create_study(n_generate=4, storage=storage)
study_summaries = get_study_summaries(storage)
assert len(study_summaries) == 1
@@ -46,7 +46,7 @@ def test_get_study_detail_is_not_preferential() -> None:
def test_get_study_summary_is_preferential() -> None:
storage = optuna.storages.InMemoryStorage()
- create_study(storage=storage)
+ create_study(n_generate=4, storage=storage)
study_summaries = get_study_summaries(storage)
assert len(study_summaries) == 1
diff --git a/standalone_app/github-pages.html b/standalone_app/github-pages.html
new file mode 100644
index 00000000..609c5c30
--- /dev/null
+++ b/standalone_app/github-pages.html
@@ -0,0 +1,19 @@
+
+
+
+
+ Optuna Dashboard (Wasm ver.)
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/standalone_app/webpack.config.js b/standalone_app/webpack.config.js
index 39caaf7e..1e876959 100644
--- a/standalone_app/webpack.config.js
+++ b/standalone_app/webpack.config.js
@@ -3,6 +3,10 @@ const path = require('path');
const mode = process.env.NODE_ENV === 'production' ? 'production' : 'development';
const isDev = mode === 'development';
+const PUBLIC_PATH = process.env.PUBLIC_PATH ?? '/public/';
+if (!PUBLIC_PATH.endsWith('/')) {
+ PUBLIC_PATH += '/';
+}
const typeScriptLoader = process.env.TYPESCRIPT_LOADER === "esbuild-loader" ? {
test: /\.tsx?$/,
@@ -34,7 +38,7 @@ var config = [
output: {
path: __dirname + '/public/',
filename: 'bundle.js',
- publicPath: '/public/'
+ publicPath: PUBLIC_PATH
},
module: {
rules: [{ oneOf: [typeScriptLoader] }]