diff --git a/.github/workflows/e2e-tests.yml b/.github/workflows/e2e-dashboard-tests.yml similarity index 90% rename from .github/workflows/e2e-tests.yml rename to .github/workflows/e2e-dashboard-tests.yml index 4665f676..c8912b9a 100644 --- a/.github/workflows/e2e-tests.yml +++ b/.github/workflows/e2e-dashboard-tests.yml @@ -1,10 +1,10 @@ -name: e2e-tests +name: e2e-dashboard-tests on: pull_request: branches: - main paths: - - '.github/workflows/e2e-tests.yml' + - '.github/workflows/e2e-dashboard-tests.yml' - '**.py' - '**.ts' - '**.tsx' @@ -47,4 +47,4 @@ jobs: run: playwright install - name: Run e2e tests - run: pytest e2e_tests + run: pytest e2e_tests/test_dashboard diff --git a/.github/workflows/e2e-standalone-tests.yml b/.github/workflows/e2e-standalone-tests.yml new file mode 100644 index 00000000..2de6d610 --- /dev/null +++ b/.github/workflows/e2e-standalone-tests.yml @@ -0,0 +1,51 @@ +name: e2e-standalone-tests +on: + pull_request: + branches: + - main + paths: + - '.github/workflows/e2e-standalone-tests.yml' + - '**.py' + - '**.ts' + - '**.tsx' + - 'package.json' + - 'package-lock.json' + - 'tsconfig.json' +jobs: + test: + runs-on: ubuntu-20.04 + strategy: + matrix: + optuna-version: ['optuna==2.10.0', 'git+https://github.com/optuna/optuna.git'] + steps: + - uses: actions/checkout@v3 + + - name: Install Rust toolchains + uses: dtolnay/rust-toolchain@stable + + - name: Install wasm-pack + run: curl https://rustwasm.github.io/wasm-pack/installer/init.sh -sSf | sh + + - name: Setup Node + uses: actions/setup-node@v2 + with: + node-version: '18' + + - name: Setup Optuna ${{ matrix.optuna-version }} + run: | + python -m pip install --progress-bar off --upgrade pip setuptools + python -m pip install --progress-bar off --upgrade ${{ matrix.optuna-version }} + + - name: Install dependencies + run: | + python -m pip install --progress-bar off . + python -m pip install --progress-bar off pytest-playwright + + - name: Build standalone_app + run: make MODE="prd" standalone_app/public/bundle.js + + - name: Install the required browsers + run: playwright install + + - name: Run e2e tests + run: pytest e2e_tests/test_standalone diff --git a/.github/workflows/python-tests.yml b/.github/workflows/python-tests.yml index 8fee4e44..69923d04 100644 --- a/.github/workflows/python-tests.yml +++ b/.github/workflows/python-tests.yml @@ -6,6 +6,7 @@ on: paths: - '.github/workflows/python-tests.yml' - '**.py' + - 'pyproject.toml' jobs: lint: runs-on: ubuntu-latest diff --git a/README.md b/README.md index 62edd6c8..badea560 100644 --- a/README.md +++ b/README.md @@ -77,6 +77,18 @@ $ docker run -it --rm -p 8080:8080 ghcr.io/optuna/optuna-dashboard postgresql+ps +## Jupyter Lab Extension (Experimental) + +You can install the Jupyter Lab extension via [PyPI](https://pypi.org/project/jupyterlab-optuna/). + +``` +$ pip install jupyterlab jupyterlab-optuna +``` + +Jupyter Lab Extension + +To use, click the tile to launch the extension, and enter your Optuna’s storage URL (e.g. `sqlite:///db.sqlite3`) in the dialog. + ## Browser-only version (Experimental) Browser-only version @@ -93,7 +105,7 @@ https://optuna.github.io/optuna-dashboard/ You can install the VS Code extension via [Visual Studio Marketplace](https://marketplace.visualstudio.com/items?itemName=Optuna.optuna-dashboard#overview). -VSCode Extension +VS Code Extension 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. This extension leverages the browser-only version of Optuna Dashboard, so the same limitations apply. diff --git a/docs/_static/jupyterlab-extension.png b/docs/_static/jupyterlab-extension.png new file mode 100644 index 00000000..7652e87f Binary files /dev/null and b/docs/_static/jupyterlab-extension.png differ diff --git a/docs/getting-started.rst b/docs/getting-started.rst index 295dd55c..d3b040c6 100644 --- a/docs/getting-started.rst +++ b/docs/getting-started.rst @@ -178,6 +178,19 @@ or $ pip install uwsgi $ uwsgi --http :8080 --workeers 4 --wsgi-file wsgi.py +Jupyter Lab Extension (Experimental) +-------------------------------- + +You can install the Jupyter Lab extension via `PyPI `_. + +.. figure:: _static/jupyterlab-extension.png + :alt: Screenshot for the Jupyter Lab Extension + :align: center + :width: 800px + + Jupyter Lab Extension + +To use, click the tile to launch the extension, and enter your Optuna’s storage URL (e.g. ``sqlite:///db.sqlite3``) in the dialog. Browser-only version (Experimental) ----------------------------------- diff --git a/docs/tutorials/preferential-optimization.rst b/docs/tutorials/preferential-optimization.rst index 4d2644ea..a192e883 100644 --- a/docs/tutorials/preferential-optimization.rst +++ b/docs/tutorials/preferential-optimization.rst @@ -27,7 +27,7 @@ First, ensure the necessary packages are installed by executing the following co .. code-block:: console - $ pip install "optuna>=3.3.0" "optuna-dashboard>=0.13.0b1" pillow botorch + $ pip install "optuna>=3.3.0" "optuna-dashboard[preferential]>=0.13.0b1" pillow Next, execute the Python script, copied from `generator.py`_. diff --git a/e2e_tests/test_usecases/__init__.py b/e2e_tests/test_dashboard/__init__.py similarity index 100% rename from e2e_tests/test_usecases/__init__.py rename to e2e_tests/test_dashboard/__init__.py diff --git a/e2e_tests/test_dashboard/test_usecases/__init__.py b/e2e_tests/test_dashboard/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_dashboard/test_usecases/test_study_history.py similarity index 97% rename from e2e_tests/test_usecases/test_study_history.py rename to e2e_tests/test_dashboard/test_usecases/test_study_history.py index e37b8f5a..6c22b16b 100644 --- a/e2e_tests/test_usecases/test_study_history.py +++ b/e2e_tests/test_dashboard/test_usecases/test_study_history.py @@ -2,7 +2,7 @@ import optuna from playwright.sync_api import Page import pytest -from ..test_server import make_test_server +from ...test_server import make_test_server def make_test_storage() -> optuna.storages.InMemoryStorage: diff --git a/e2e_tests/visual_regression_test.py b/e2e_tests/test_dashboard/visual_regression_test.py similarity index 99% rename from e2e_tests/visual_regression_test.py rename to e2e_tests/test_dashboard/visual_regression_test.py index d83fbb5f..ea6ff930 100644 --- a/e2e_tests/visual_regression_test.py +++ b/e2e_tests/test_dashboard/visual_regression_test.py @@ -4,7 +4,7 @@ import optuna from playwright.sync_api import Page import pytest -from .test_server import make_test_server +from ..test_server import make_test_server @pytest.fixture diff --git a/e2e_tests/test_server.py b/e2e_tests/test_server.py index b6d14734..a15acc68 100644 --- a/e2e_tests/test_server.py +++ b/e2e_tests/test_server.py @@ -1,4 +1,6 @@ +import http.server import socket +import socketserver import threading from wsgiref.simple_server import make_server @@ -33,3 +35,29 @@ def make_test_server( request.addfinalizer(stop_server) return f"http://{addr}:{port}/dashboard" + + +def make_standalone_server(request: pytest.FixtureRequest) -> str: + addr = "127.0.0.1" + port = get_free_port() + directory = "./standalone_app/" + + Handler = http.server.SimpleHTTPRequestHandler + httpd = socketserver.TCPServer( + ("", port), lambda *args, **kwargs: Handler(*args, directory=directory, **kwargs) + ) + + def serve_httpd(): + httpd.serve_forever() + + thread = threading.Thread(target=serve_httpd) + thread.start() + + def stop_server() -> None: + httpd.shutdown() + httpd.server_close() + thread.join() + + request.addfinalizer(stop_server) + + return f"http://{addr}:{port}" diff --git a/e2e_tests/test_standalone/__init__.py b/e2e_tests/test_standalone/__init__.py new file mode 100644 index 00000000..e69de29b diff --git a/e2e_tests/test_standalone/test_study_list.py b/e2e_tests/test_standalone/test_study_list.py new file mode 100644 index 00000000..b0d4735e --- /dev/null +++ b/e2e_tests/test_standalone/test_study_list.py @@ -0,0 +1,22 @@ +from playwright.sync_api import Page +import pytest + +from ..test_server import make_standalone_server + + +@pytest.fixture +def server_url(request: pytest.FixtureRequest) -> str: + return make_standalone_server(request) + + +def test_home( + page: Page, + server_url: str, +) -> None: + url = f"{server_url}" + page.goto(url) + element = page.get_by_role("heading") + assert element is not None + title = element.text_content() + assert title is not None + assert title == "Optuna Dashboard (Wasm ver.)" diff --git a/optuna_dashboard/__init__.py b/optuna_dashboard/__init__.py index ea2a8dbe..8b793e62 100644 --- a/optuna_dashboard/__init__.py +++ b/optuna_dashboard/__init__.py @@ -17,4 +17,4 @@ from ._note import save_note # noqa from ._preference_setting import register_preference_feedback_component # noqa -__version__ = "0.13.0b1" +__version__ = "0.13.0" diff --git a/optuna_dashboard/preferential/_study.py b/optuna_dashboard/preferential/_study.py index d715b97b..101b61de 100644 --- a/optuna_dashboard/preferential/_study.py +++ b/optuna_dashboard/preferential/_study.py @@ -7,9 +7,9 @@ from typing import Iterable import optuna from optuna import logging +from optuna._imports import try_import from optuna.distributions import BaseDistribution 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 @@ -20,6 +20,10 @@ from optuna_dashboard.preferential._system_attrs import report_preferences from optuna_dashboard.preferential._system_attrs import set_n_generate +with try_import() as _imports: + from optuna_dashboard.preferential.samplers.gp import PreferentialGPSampler + + _logger = logging.get_logger(__name__) _SYSTEM_ATTR_PREFERENTIAL_STUDY = "preference:is_preferential" @@ -344,11 +348,10 @@ def create_study( sampler: A sampler object that implements background algorithm for value suggestion. - If :obj:`None` is specified, `RandomSampler`_ is used. Please note that - most Optuna samplers does not work efficiently for preferential optimization. - - .. _RandomSampler: https://optuna.readthedocs.io/en/stable/reference/\ - samplers/generated/optuna.samplers.RandomSampler.html + If :obj:`None` is specified, + :class:`~optuna_dashboard.preferential.samplers.gp.PreferentialGPSampler` is used. + Please note that most Optuna samplers does not work efficiently for preferential + optimization. study_name: Study's name. If this argument is set to None, a unique name is generated @@ -369,9 +372,13 @@ def create_study( The interface may change in newer versions without prior notice. """ try: + if sampler is None: + _imports.check() # If BoTorch is not installed, raise ImportError. + sampler = PreferentialGPSampler() + study = optuna.create_study( storage=storage, - sampler=sampler or RandomSampler(), + sampler=sampler, study_name=study_name, ) study._storage.set_study_system_attr( @@ -441,11 +448,10 @@ def load_study( :func:`~optuna.study.create_study` for further details. sampler: A sampler object that implements background algorithm for value suggestion. - If :obj:`None` is specified, `RandomSampler`_ is used. Please note that - most Optuna samplers does not work efficiently for preferential optimization. - - .. _RandomSampler: https://optuna.readthedocs.io/en/stable/reference/samplers/\ - generated/optuna.samplers.RandomSampler.html + If :obj:`None` is specified, + :class:`~optuna_dashboard.preferential.samplers.gp.PreferentialGPSampler` is used. + Please note that most Optuna samplers does not work efficiently for preferential + optimization. Returns: A :class:`~optuna_dashboard.preferential.PreferentialStudy` object. @@ -454,9 +460,11 @@ def load_study( Preferential optimization is an experimental feature (introduced in v0.13.0). The interface may change in newer versions without prior notice. """ - study = optuna.load_study( - study_name=study_name, storage=storage, sampler=sampler or RandomSampler() - ) + if sampler is None: + _imports.check() # If BoTorch is not installed, raise ImportError. + sampler = PreferentialGPSampler() + + study = optuna.load_study(study_name=study_name, storage=storage, sampler=sampler) system_attrs = study._storage.get_study_system_attrs(study._study_id) if not system_attrs.get(_SYSTEM_ATTR_PREFERENTIAL_STUDY): raise ValueError("The study is not a PreferentialStudy.") diff --git a/optuna_dashboard/ts/components/PreferenceHistory.tsx b/optuna_dashboard/ts/components/PreferenceHistory.tsx index 6aa67317..b7bd04b4 100644 --- a/optuna_dashboard/ts/components/PreferenceHistory.tsx +++ b/optuna_dashboard/ts/components/PreferenceHistory.tsx @@ -147,6 +147,16 @@ const CandidateTrial: FC<{ overflow: "auto", }} > + setDetailShown(false)} + > + + false} diff --git a/optuna_dashboard/ts/components/StudyList.tsx b/optuna_dashboard/ts/components/StudyList.tsx index c41c51cc..14eaa68b 100644 --- a/optuna_dashboard/ts/components/StudyList.tsx +++ b/optuna_dashboard/ts/components/StudyList.tsx @@ -64,8 +64,7 @@ export const StudyList: FC<{ return useMemo(() => new URLSearchParams(search), [search]) } const query = useQuery() - const initialSortBy = - query.get("studies_order_by") === "desc" ? "desc" : "asc" + const initialSortBy = query.get("studies_order_by") === "asc" ? "asc" : "desc" const [sortBy, setSortBy] = useState<"asc" | "desc">(initialSortBy) let filteredStudies = studies.filter((s) => !studyFilter(s)) diff --git a/optuna_dashboard/ts/components/ThreejsArtifactViewer.tsx b/optuna_dashboard/ts/components/ThreejsArtifactViewer.tsx index 3c4e2e01..f04a269e 100644 --- a/optuna_dashboard/ts/components/ThreejsArtifactViewer.tsx +++ b/optuna_dashboard/ts/components/ThreejsArtifactViewer.tsx @@ -5,7 +5,9 @@ import { GizmoHelper, GizmoViewport, OrbitControls } from "@react-three/drei" import { STLLoader } from "three/examples/jsm/loaders/STLLoader" import { Rhino3dmLoader } from "three/examples/jsm/loaders/3DMLoader" import { PerspectiveCamera } from "three" -import { Modal, Box } from "@mui/material" +import { Modal, Box, useTheme } from "@mui/material" +import ClearIcon from "@mui/icons-material/Clear" +import IconButton from "@mui/material/IconButton" export const isThreejsArtifact = (artifact: Artifact): boolean => { return ( @@ -32,7 +34,7 @@ const CustomGizmoHelper: React.FC = () => { ) } -const calculateBoundingBox = (geometries: THREE.BufferGeometry[]) => { +const computeBoundingBox = (geometries: THREE.BufferGeometry[]) => { const boundingBox = new THREE.Box3() geometries.forEach((geometry) => { const mesh = new THREE.Mesh(geometry) @@ -45,8 +47,11 @@ export const ThreejsArtifactViewer: React.FC = ( props ) => { const [geometry, setGeometry] = useState([]) - const [modelSize, setModelSize] = useState( - new THREE.Vector3(10, 10, 10) + const [boundingBox, setBoundingBox] = useState( + new THREE.Box3( + new THREE.Vector3(-10, -10, -10), + new THREE.Vector3(10, 10, 10) + ) ) const [cameraSettings, setCameraSettings] = useState( new THREE.PerspectiveCamera() @@ -54,11 +59,11 @@ export const ThreejsArtifactViewer: React.FC = ( const handleLoadedGeometries = (geometries: THREE.BufferGeometry[]) => { setGeometry(geometries) - const boundingBox = calculateBoundingBox(geometries) + const boundingBox = computeBoundingBox(geometries) if (boundingBox !== null) { - const size = boundingBox.getSize(new THREE.Vector3()) - setModelSize(size) + setBoundingBox(boundingBox) } + return boundingBox } useEffect(() => { @@ -81,20 +86,30 @@ export const ThreejsArtifactViewer: React.FC = ( } }) } - const maxModelSize = Math.max(modelSize.x, modelSize.y, modelSize.z) - const cameraSet = new THREE.PerspectiveCamera( - modelSize - ? Math.min( - 45, - Math.atan(modelSize.y / modelSize.z) * (180 / Math.PI) * 2 - ) - : 45, - window.innerWidth / window.innerHeight - ) - cameraSet.position.set(maxModelSize * 2, maxModelSize * 2, maxModelSize * 2) - setCameraSettings(cameraSet) }, []) + useEffect(() => { + const cameraSet = new THREE.PerspectiveCamera( + 50, + window.innerWidth / window.innerHeight, + 0.1, + boundingBox.getSize(new THREE.Vector3()).length() * 100 + ) + const maxPosition = Math.max( + boundingBox.max.x, + boundingBox.max.y, + boundingBox.max.z + ) + cameraSet.position.set( + maxPosition * 1.5, + maxPosition * 1.5, + maxPosition * 1.5 + ) + const center = boundingBox.getCenter(new THREE.Vector3()) + cameraSet.lookAt(center.x, center.y, center.z) + setCameraSettings(cameraSet) + }, [boundingBox]) + return ( = ( > - + {props.hasGizmo && } {geometry.length > 0 && @@ -121,6 +136,7 @@ export const useThreejsArtifactModal = (): [ ] => { const [open, setOpen] = useState(false) const [target, setTarget] = useState<[string, Artifact | null]>(["", null]) + const theme = useTheme() const openModal = (artifactUrlPath: string, artifact: Artifact) => { setTarget([artifactUrlPath, artifact]) @@ -146,6 +162,19 @@ export const useThreejsArtifactModal = (): [ borderRadius: "15px", }} > + { + setOpen(false) + setTarget(["", null]) + }} + > + + =6.9.0" @@ -125,13 +126,14 @@ } }, "node_modules/@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "dependencies": { - "@babel/types": "^7.20.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "engines": { @@ -250,9 +252,9 @@ } }, "node_modules/@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true, "engines": { "node": ">=6.9.0" @@ -271,25 +273,25 @@ } }, "node_modules/@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "dependencies": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" @@ -417,29 +419,29 @@ } }, "node_modules/@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "dependencies": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", "engines": { "node": ">=6.9.0" } @@ -483,12 +485,12 @@ } }, "node_modules/@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "dependencies": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" }, "engines": { @@ -496,9 +498,9 @@ } }, "node_modules/@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true, "bin": { "parser": "bin/babel-parser.js" @@ -1657,33 +1659,33 @@ } }, "node_modules/@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "dependencies": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" }, @@ -1692,12 +1694,12 @@ } }, "node_modules/@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "dependencies": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" }, "engines": { @@ -15313,11 +15315,12 @@ } }, "@babel/code-frame": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.18.6.tgz", - "integrity": "sha512-TDCmlK5eOvH+eH7cdAFlNXeVJqWIQ7gW9tY1GJIpUtFb6CmjVyq2VM3u71bOyR8CRihcCgMUYoDNyLXao3+70Q==", + "version": "7.22.13", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", + "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", "requires": { - "@babel/highlight": "^7.18.6" + "@babel/highlight": "^7.22.13", + "chalk": "^2.4.2" } }, "@babel/compat-data": { @@ -15350,13 +15353,14 @@ } }, "@babel/generator": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.20.5.tgz", - "integrity": "sha512-jl7JY2Ykn9S0yj4DQP82sYvPU+T3g0HFcWTqDLqiuA9tGRNIj9VfbtXGAYTTkyNEnQk1jkMGOdYka8aG/lulCA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", + "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", "dev": true, "requires": { - "@babel/types": "^7.20.5", + "@babel/types": "^7.23.0", "@jridgewell/gen-mapping": "^0.3.2", + "@jridgewell/trace-mapping": "^0.3.17", "jsesc": "^2.5.1" }, "dependencies": { @@ -15444,9 +15448,9 @@ } }, "@babel/helper-environment-visitor": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.18.9.tgz", - "integrity": "sha512-3r/aACDJ3fhQ/EVgFy0hpj8oHyHpQc+LPtJoY9SzTThAsStm4Ptegq92vqKoE3vD706ZVFWITnMnxucw+S9Ipg==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", + "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", "dev": true }, "@babel/helper-explode-assignable-expression": { @@ -15459,22 +15463,22 @@ } }, "@babel/helper-function-name": { - "version": "7.19.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.19.0.tgz", - "integrity": "sha512-WAwHBINyrpqywkUH0nTnNgI5ina5TFn85HKS0pbPDfxFfhyR/aNQEn4hGi1P1JyT//I0t4OgXUlofzWILRvS5w==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", + "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", "dev": true, "requires": { - "@babel/template": "^7.18.10", - "@babel/types": "^7.19.0" + "@babel/template": "^7.22.15", + "@babel/types": "^7.23.0" } }, "@babel/helper-hoist-variables": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.18.6.tgz", - "integrity": "sha512-UlJQPkFqFULIcyW5sbzgbkxn2FKRgwWiRexcuaR8RNJRy8+LLveqPjwZV/bwrLZCN0eUHD/x8D0heK1ozuoo6Q==", + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", + "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-member-expression-to-functions": { @@ -15569,23 +15573,23 @@ } }, "@babel/helper-split-export-declaration": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.18.6.tgz", - "integrity": "sha512-bde1etTx6ZyTmobl9LLMMQsaizFVZrquTEHOqKeQESMKo4PlObf+8+JA25ZsIpZhT/WEd39+vOdLXAFG/nELpA==", + "version": "7.22.6", + "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", + "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", "dev": true, "requires": { - "@babel/types": "^7.18.6" + "@babel/types": "^7.22.5" } }, "@babel/helper-string-parser": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz", - "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==" + "version": "7.22.5", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", + "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==" }, "@babel/helper-validator-identifier": { - "version": "7.19.1", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz", - "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==" + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", + "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==" }, "@babel/helper-validator-option": { "version": "7.18.6", @@ -15617,19 +15621,19 @@ } }, "@babel/highlight": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.18.6.tgz", - "integrity": "sha512-u7stbOuYjaPezCuLj29hNW1v64M2Md2qupEKP1fHc7WdOA3DgLh37suiSrZYY7haUB7iBeQZ9P1uiRF359do3g==", + "version": "7.22.20", + "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", + "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", "requires": { - "@babel/helper-validator-identifier": "^7.18.6", - "chalk": "^2.0.0", + "@babel/helper-validator-identifier": "^7.22.20", + "chalk": "^2.4.2", "js-tokens": "^4.0.0" } }, "@babel/parser": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.20.5.tgz", - "integrity": "sha512-r27t/cy/m9uKLXQNWWebeCUHgnAZq0CpG1OwKRxzJMP1vpSU4bSIK2hq+/cp0bQxetkXx38n09rNu8jVkcK/zA==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", + "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", "dev": true }, "@babel/plugin-bugfix-safari-id-destructuring-collision-in-function-expression": { @@ -16401,41 +16405,41 @@ } }, "@babel/template": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.18.10.tgz", - "integrity": "sha512-TI+rCtooWHr3QJ27kJxfjutghu44DLnasDMwpDqCXVTal9RLp3RSYNh4NdBrRP2cQAoG9A8juOQl6P6oZG4JxA==", + "version": "7.22.15", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", + "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/parser": "^7.18.10", - "@babel/types": "^7.18.10" + "@babel/code-frame": "^7.22.13", + "@babel/parser": "^7.22.15", + "@babel/types": "^7.22.15" } }, "@babel/traverse": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.20.5.tgz", - "integrity": "sha512-WM5ZNN3JITQIq9tFZaw1ojLU3WgWdtkxnhM1AegMS+PvHjkM5IXjmYEGY7yukz5XS4sJyEf2VzWjI8uAavhxBQ==", + "version": "7.23.2", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", + "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", "dev": true, "requires": { - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.20.5", - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-function-name": "^7.19.0", - "@babel/helper-hoist-variables": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/parser": "^7.20.5", - "@babel/types": "^7.20.5", + "@babel/code-frame": "^7.22.13", + "@babel/generator": "^7.23.0", + "@babel/helper-environment-visitor": "^7.22.20", + "@babel/helper-function-name": "^7.23.0", + "@babel/helper-hoist-variables": "^7.22.5", + "@babel/helper-split-export-declaration": "^7.22.6", + "@babel/parser": "^7.23.0", + "@babel/types": "^7.23.0", "debug": "^4.1.0", "globals": "^11.1.0" } }, "@babel/types": { - "version": "7.20.5", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.20.5.tgz", - "integrity": "sha512-c9fst/h2/dcF7H+MJKZ2T0KjEQ8hY/BNnDk/H3XY8C4Aw/eWQXWn/lWntHF9ooUBnGmEvbfGrTgLWc+um0YDUg==", + "version": "7.23.0", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", + "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", "requires": { - "@babel/helper-string-parser": "^7.19.4", - "@babel/helper-validator-identifier": "^7.19.1", + "@babel/helper-string-parser": "^7.22.5", + "@babel/helper-validator-identifier": "^7.22.20", "to-fast-properties": "^2.0.0" } }, diff --git a/pyproject.toml b/pyproject.toml index 9d8500dc..1f125735 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -52,9 +52,12 @@ test = [ optional = [ "streamlit", "boto3", - "botorch", + "botorch>=0.8.1; python_version>='3.8'", ] +preferential = [ + "botorch>=0.8.1", +] [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 7fc1aaba..9b4a3f31 100644 --- a/python_tests/preferential/test_study.py +++ b/python_tests/preferential/test_study.py @@ -3,6 +3,7 @@ from __future__ import annotations import copy import multiprocessing import pickle +import sys from typing import Callable from unittest.mock import patch import uuid @@ -22,6 +23,10 @@ from ..storage_supplier import parametrize_storages from ..storage_supplier import StorageSupplier +if sys.version_info < (3, 8): + pytest.skip("BoTorch dropped Python3.7 support", allow_module_level=True) + + @parametrize_storages def test_study_set_and_get_user_attrs(storage_supplier: Callable[[], StorageSupplier]) -> None: with storage_supplier() as storage: diff --git a/python_tests/test_api.py b/python_tests/test_api.py index f9c56e4f..bde7838a 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import sys from unittest import TestCase import optuna @@ -14,6 +15,7 @@ from optuna_dashboard._preferential_history import remove_history from optuna_dashboard._preferential_history import report_history from optuna_dashboard._serializer import serialize_preference_history from optuna_dashboard.preferential import create_study +import pytest from .wsgi_client import send_request @@ -105,6 +107,7 @@ class APITestCase(TestCase): ) self.assertEqual(status, 400) + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_get_best_trials_of_preferential_study(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(n_generate=4, storage=storage) @@ -128,6 +131,7 @@ class APITestCase(TestCase): assert len(best_trials) == 1 assert best_trials[0]["number"] == 0 + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_report_preference(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(n_generate=4, storage=storage) @@ -161,6 +165,7 @@ class APITestCase(TestCase): assert better.number == 2 assert worse.number == 1 + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_report_preference_when_typo_mode(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(storage=storage, n_generate=3) @@ -184,6 +189,7 @@ class APITestCase(TestCase): ) self.assertEqual(status, 400) + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_change_component(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(storage=storage, n_generate=3) @@ -214,6 +220,7 @@ class APITestCase(TestCase): assert study_detail["feedback_component_type"]["output_type"] == "artifact" assert study_detail["feedback_component_type"]["artifact_key"] == "image" + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_skip_trial(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(n_generate=4, storage=storage) @@ -238,6 +245,7 @@ class APITestCase(TestCase): assert len(best_trials) == 1 assert best_trials[0].number == 2 + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_remove_history(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(storage=storage, n_generate=3) @@ -271,6 +279,7 @@ class APITestCase(TestCase): assert histories[0]["is_removed"] assert len(study.get_preferences()) == 0 + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_restore_history(self) -> None: storage = optuna.storages.InMemoryStorage() study = create_study(storage=storage, n_generate=3) diff --git a/python_tests/test_preferential_history.py b/python_tests/test_preferential_history.py index 3d1a7d70..dcae4797 100644 --- a/python_tests/test_preferential_history.py +++ b/python_tests/test_preferential_history.py @@ -1,6 +1,7 @@ from __future__ import annotations import json +import sys from typing import Callable from typing import TYPE_CHECKING @@ -13,6 +14,7 @@ from optuna_dashboard._preferential_history import restore_history from optuna_dashboard._serializer import serialize_preference_history from optuna_dashboard.preferential import create_study from optuna_dashboard.preferential._system_attrs import _SYSTEM_ATTR_PREFIX_PREFERENCE +import pytest from .storage_supplier import parametrize_storages from .storage_supplier import StorageSupplier @@ -22,6 +24,10 @@ if TYPE_CHECKING: from optuna_dashboard._preferential_history import History +if sys.version_info < (3, 8): + pytest.skip("BoTorch dropped Python3.7 support", allow_module_level=True) + + @parametrize_storages def test_report_and_get_choices(storage_supplier: Callable[[], StorageSupplier]) -> None: with storage_supplier() as storage: diff --git a/python_tests/test_serializers.py b/python_tests/test_serializers.py index ea1c3517..0d1546d2 100644 --- a/python_tests/test_serializers.py +++ b/python_tests/test_serializers.py @@ -1,11 +1,14 @@ from __future__ import annotations +import sys + import optuna from optuna_dashboard._serializer import serialize_attrs from optuna_dashboard._serializer import serialize_study_detail from optuna_dashboard._serializer import serialize_study_summary from optuna_dashboard._storage import get_study_summaries from optuna_dashboard.preferential import create_study +import pytest def test_serialize_bytes() -> None: @@ -22,6 +25,7 @@ def test_serialize_dict() -> None: assert len(serialized) <= 1 +@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_get_study_detail_is_preferential() -> None: storage = optuna.storages.InMemoryStorage() study = create_study(n_generate=4, storage=storage) @@ -48,6 +52,7 @@ def test_get_study_detail_is_not_preferential() -> None: assert not study_detail["is_preferential"] +@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_get_study_summary_is_preferential() -> None: storage = optuna.storages.InMemoryStorage() create_study(n_generate=4, storage=storage) diff --git a/rustlib/src/lib.rs b/rustlib/src/lib.rs index ff09ef81..c326e503 100644 --- a/rustlib/src/lib.rs +++ b/rustlib/src/lib.rs @@ -1,16 +1,20 @@ +use fanova::{FanovaOptions, RandomForestOptions}; use js_sys::Array; use serde_wasm_bindgen::from_value; -use fanova::{FanovaOptions, RandomForestOptions}; use wasm_bindgen::prelude::*; #[wasm_bindgen] -pub fn wasm_fanova_calculate(features: Array, targets: Array) -> Vec { - // TODO(c-bata): Fix error handling +pub fn wasm_fanova_calculate(features: Array, targets: Array) -> Result, JsError> { let features_vec: Vec> = features .iter() - .map(|x| from_value::>(x).unwrap()) - .collect(); - let targets_vec: Vec = targets.iter().map(|x| x.as_f64().unwrap()).collect(); + .map(|x| from_value::>(x)) + .collect::>() + .map_err(|_| JsError::new("features must be of type number[][]"))?; + let targets_vec: Vec = targets + .iter() + .map(|x| x.as_f64()) + .collect::>() + .ok_or(JsError::new("targets must be of type number[]"))?; let mut fanova = FanovaOptions::new() .random_forest(RandomForestOptions::new().seed(0)) @@ -18,9 +22,10 @@ pub fn wasm_fanova_calculate(features: Array, targets: Array) -> Vec { features_vec.iter().map(|x| x.as_slice()).collect(), &targets_vec, ) - .unwrap(); + .map_err(|e| JsError::new(&format!("failed to build fANOVA model: {}", e)))?; let importances = (0..features_vec.len()) .map(|i| fanova.quantify_importance(&[i]).mean) .collect::>(); - return importances; + + Ok(importances) } diff --git a/standalone_app/src/components/PlotImportance.tsx b/standalone_app/src/components/PlotImportance.tsx index 24e9cd8f..7303b2db 100644 --- a/standalone_app/src/components/PlotImportance.tsx +++ b/standalone_app/src/components/PlotImportance.tsx @@ -39,6 +39,7 @@ export const PlotImportance: FC<{ study: Study }> = ({ study }) => { const values = filteredTrials.map( (t) => t.values?.[objectiveId] as number ) + // TODO: handle errors thrown by wasm_fanova_calculate const importance = wasm_fanova_calculate(features, values) return study.intersection_search_space.map((s, i) => ({ name: s.name, diff --git a/vscode/README.md b/vscode/README.md index 78587c37..960669a3 100644 --- a/vscode/README.md +++ b/vscode/README.md @@ -14,6 +14,10 @@ Nothing to configure. ## Release Notes +### 0.1.0 + +Added older database schemas support (Optuna 2.6.0 or later) + ### 0.0.1 Initial Release diff --git a/vscode/package.json b/vscode/package.json index a5c1d818..6a99a905 100644 --- a/vscode/package.json +++ b/vscode/package.json @@ -3,7 +3,7 @@ "displayName": "Optuna Dashboard", "description": "Web Dashboard for Optuna", "publisher": "Optuna", - "version": "0.0.1", + "version": "0.1.0", "license": "MIT", "icon": "images/optuna-logo.png", "engines": {