mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-12 12:40:33 +08:00
Merge branch 'main' into add_color_scale_setting
This commit is contained in:
@@ -16,7 +16,7 @@ jobs:
|
||||
runs-on: ubuntu-20.04
|
||||
strategy:
|
||||
matrix:
|
||||
optuna-version: ['optuna==2.10.0', 'git+https://github.com/optuna/optuna.git']
|
||||
optuna-version: ['optuna==3.1.0', 'git+https://github.com/optuna/optuna.git']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
|
||||
@@ -48,9 +48,4 @@ jobs:
|
||||
|
||||
- name: Run e2e tests
|
||||
run: |
|
||||
if [ "${{ matrix.optuna-version }}" = "optuna==2.10.0" ]; then
|
||||
ignore_option="--ignore e2e_tests/test_dashboard/test_usecases/test_preferential_optimization.py"
|
||||
else
|
||||
ignore_option=""
|
||||
fi
|
||||
pytest e2e_tests/test_dashboard $ignore_option
|
||||
pytest e2e_tests/test_dashboard
|
||||
|
||||
@@ -28,7 +28,6 @@ jobs:
|
||||
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
|
||||
|
||||
@@ -18,10 +18,8 @@ jobs:
|
||||
python-version: '3.10'
|
||||
architecture: x64
|
||||
- name: Install dependencies
|
||||
# python_tests requires optuna>=3.0.0 since it imports FloatDistribution
|
||||
run: |
|
||||
python -m pip install --progress-bar off --upgrade pip setuptools
|
||||
pip install --progress-bar off "optuna>=3.0.0"
|
||||
pip install --progress-bar off .
|
||||
pip install --progress-bar off flake8 black isort mypy mypy-boto3-s3
|
||||
- run: flake8 . --show-source
|
||||
@@ -32,9 +30,8 @@ jobs:
|
||||
runs-on: ubuntu-latest
|
||||
strategy:
|
||||
matrix:
|
||||
# Add Python '3.11' after released Optuna v3.1
|
||||
# since Optuna v3.0.4 depends on scipy<1.9.0 and >=1.7.0
|
||||
python-version: ['3.7', '3.8', '3.9', '3.10']
|
||||
python-version: ['3.7', '3.8', '3.9', '3.10', '3.11']
|
||||
optuna-version: ['optuna>=3.1.0']
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Python${{ matrix.python-version }}
|
||||
@@ -42,15 +39,18 @@ jobs:
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
architecture: x64
|
||||
- name: Install dependencies
|
||||
# python_tests requires optuna>=3.0.0 since it imports FloatDistribution
|
||||
- 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: |
|
||||
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
|
||||
- name: Run python unit tests
|
||||
run: |
|
||||
pytest python_tests
|
||||
test-with-optuna-master:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
@@ -67,4 +67,25 @@ jobs:
|
||||
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
|
||||
- name: Run python unit tests
|
||||
run: |
|
||||
pytest python_tests
|
||||
test-with-optuna-oldest:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v4
|
||||
with:
|
||||
python-version: '3.11'
|
||||
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 .
|
||||
python -m pip install --progress-bar off --force-reinstall optuna==3.1.0
|
||||
- name: Run python unit tests
|
||||
run: |
|
||||
pytest python_tests
|
||||
|
||||
+4
-4
@@ -1,4 +1,4 @@
|
||||
FROM node:16 AS front-builder
|
||||
FROM node:20 AS front-builder
|
||||
WORKDIR /usr/src
|
||||
|
||||
ADD ./package.json /usr/src/package.json
|
||||
@@ -11,7 +11,7 @@ ADD ./optuna_dashboard/ts/ /usr/src/optuna_dashboard/ts
|
||||
RUN mkdir -p /usr/src/optuna_dashboard/public
|
||||
RUN npm run build:prd
|
||||
|
||||
FROM python:3.10-buster AS python-builder
|
||||
FROM python:3.11-buster AS python-builder
|
||||
|
||||
WORKDIR /usr/src
|
||||
RUN pip install --upgrade pip setuptools
|
||||
@@ -23,9 +23,9 @@ ADD ./optuna_dashboard /usr/src/optuna_dashboard
|
||||
COPY --from=front-builder /usr/src/optuna_dashboard/public/ /usr/src/optuna_dashboard/public/
|
||||
RUN pip install --progress-bar off .
|
||||
|
||||
FROM python:3.10-slim-buster as runner
|
||||
FROM python:3.11-slim-buster as runner
|
||||
|
||||
COPY --from=python-builder /usr/local/lib/python3.10/site-packages /usr/local/lib/python3.10/site-packages
|
||||
COPY --from=python-builder /usr/local/lib/python3.11/site-packages /usr/local/lib/python3.11/site-packages
|
||||
COPY --from=python-builder /usr/local/bin/optuna-dashboard /usr/local/bin/optuna-dashboard
|
||||
|
||||
RUN mkdir /app
|
||||
|
||||
+1
-16
@@ -12,9 +12,9 @@ General APIs
|
||||
|
||||
optuna_dashboard.run_server
|
||||
optuna_dashboard.wsgi
|
||||
optuna_dashboard.set_objective_names
|
||||
optuna_dashboard.save_note
|
||||
optuna_dashboard.save_plotly_graph_object
|
||||
optuna_dashboard.artifact.get_artifact_path
|
||||
|
||||
Human-in-the-loop
|
||||
-----------------
|
||||
@@ -57,18 +57,3 @@ Streamlit
|
||||
optuna_dashboard.streamlit.render_trial_note
|
||||
optuna_dashboard.streamlit.render_objective_form_widgets
|
||||
optuna_dashboard.streamlit.render_user_attr_form_widgets
|
||||
|
||||
Artifact
|
||||
--------
|
||||
|
||||
.. autosummary::
|
||||
:toctree: _generated/
|
||||
:nosignatures:
|
||||
|
||||
optuna_dashboard.artifact.upload_artifact
|
||||
optuna_dashboard.artifact.file_system.FileSystemBackend
|
||||
optuna_dashboard.artifact.boto3.Boto3Backend
|
||||
optuna_dashboard.artifact.backoff.Backoff
|
||||
optuna_dashboard.artifact.prefix.AppendPrefix
|
||||
optuna_dashboard.artifact.protocol.ArtifactBackend
|
||||
optuna_dashboard.artifact.exceptions.ArtifactNotFound
|
||||
|
||||
+4
-4
@@ -27,7 +27,7 @@ or use a following dirty hack to unwrap ``_CachedStorage`` class.
|
||||
``set_objective_names()`` function is deprecated. Please use ``study.set_metric_names()`` instead.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:func:`~optuna_dashboard.set_objective_names` function has been ported to Optuna.
|
||||
``set_objective_names`` function has been ported to Optuna.
|
||||
Please use `study.set_metric_names() <https://optuna.readthedocs.io/en/latest/reference/generated/optuna.study.Study.html#optuna.study.Study>`_ function instead.
|
||||
|
||||
.. list-table::
|
||||
@@ -41,7 +41,7 @@ Please use `study.set_metric_names() <https://optuna.readthedocs.io/en/latest/re
|
||||
``upload_artifact()`` is deprecated. Please use ``optuna.artifacts.upload_artifact()`` instead.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:func:`~optuna_dashboard.artifact.upload_artifact` function has been ported to Optuna.
|
||||
``upload_artifact`` function has been ported to Optuna.
|
||||
Please use `optuna.artifacts.upload_artifact <https://optuna.readthedocs.io/en/latest/reference/generated/optuna.artifacts.upload_artifact.html>`_ function instead.
|
||||
|
||||
.. list-table::
|
||||
@@ -57,7 +57,7 @@ Please note that the order of arguments is different between the deprecated and
|
||||
``FileSystemBackend`` is deprecated. Please use ``FileSystemArtifactStore`` instead.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:class:`~optuna_dashboard.artifact.file_system.FileSystemBackend` class has been ported to Optuna.
|
||||
``FileSystemBackend`` class has been ported to Optuna.
|
||||
Please use `FileSystemArtifactStore <https://optuna.readthedocs.io/en/latest/reference/generated/optuna.artifacts.FileSystemArtifactStore.html>`_ class instead.
|
||||
|
||||
.. list-table::
|
||||
@@ -71,7 +71,7 @@ Please use `FileSystemArtifactStore <https://optuna.readthedocs.io/en/latest/ref
|
||||
``Boto3Backend``` is deprecated. Please use ``Boto3ArtifactStore`` instead.
|
||||
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
:class:`~optuna_dashboard.artifact.boto3.Boto3Backend` class has been ported to Optuna.
|
||||
``Boto3Backend`` class has been ported to Optuna.
|
||||
Please use `Boto3ArtifactStore <https://optuna.readthedocs.io/en/latest/reference/generated/optuna.artifacts.Boto3ArtifactStore.html>`_ class instead.
|
||||
|
||||
.. list-table::
|
||||
|
||||
@@ -1,44 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=$(cd $(dirname $0); pwd)
|
||||
OPTUNA_DASHBOARD_IMAGE=${OPTUNA_DASHBOARD_IMAGE:-cbata/optuna-dashboard}
|
||||
|
||||
echo ""
|
||||
echo "1. Prepare MYSQL 8.0 Server using Docker."
|
||||
echo ""
|
||||
|
||||
set -e
|
||||
docker run \
|
||||
-d \
|
||||
--rm \
|
||||
-p 3306:3306 \
|
||||
-e MYSQL_USER=optuna \
|
||||
-e MYSQL_DATABASE=optuna \
|
||||
-e MYSQL_PASSWORD=password \
|
||||
-e MYSQL_ALLOW_EMPTY_PASSWORD=yes \
|
||||
--name optuna-mysql \
|
||||
mysql:8
|
||||
|
||||
echo "Wait ready for MySQL"
|
||||
sleep 20
|
||||
|
||||
|
||||
echo ""
|
||||
echo "2. Run an Optuna example."
|
||||
echo ""
|
||||
|
||||
python ${DIR}/optuna_example.py "mysql+pymysql://optuna:password@127.0.0.1:3306/optuna"
|
||||
|
||||
set +e
|
||||
|
||||
echo ""
|
||||
echo "3. Open Web Dashboard"
|
||||
echo ""
|
||||
|
||||
docker run -it --rm -p 8080:8080 --link=optuna-mysql $OPTUNA_DASHBOARD_IMAGE "mysql+pymysql://optuna:password@optuna-mysql:3306/optuna"
|
||||
|
||||
echo ""
|
||||
echo "4. Stop MYSQL Server"
|
||||
echo ""
|
||||
|
||||
docker stop optuna-mysql
|
||||
@@ -1,43 +0,0 @@
|
||||
#!/bin/sh
|
||||
|
||||
DIR=$(cd $(dirname $0); pwd)
|
||||
OPTUNA_DASHBOARD_IMAGE=${OPTUNA_DASHBOARD_IMAGE:-cbata/optuna-dashboard}
|
||||
|
||||
echo ""
|
||||
echo "1. Run PostgreSQL Server using Docker."
|
||||
echo ""
|
||||
|
||||
set -e
|
||||
docker run \
|
||||
-d \
|
||||
--rm \
|
||||
-p 5432:5432 \
|
||||
-e POSTGRES_USER=optuna \
|
||||
-e POSTGRES_DB=optuna \
|
||||
-e POSTGRES_PASSWORD=password \
|
||||
--name optuna-postgres \
|
||||
postgres:10.1-alpine
|
||||
|
||||
echo "Wait ready for PostgreSQL"
|
||||
sleep 20
|
||||
|
||||
|
||||
echo ""
|
||||
echo "2. Run an Optuna example."
|
||||
echo ""
|
||||
|
||||
python ${DIR}/optuna_example.py "postgresql+psycopg2://optuna:password@127.0.0.1:5432/optuna"
|
||||
|
||||
set +e
|
||||
|
||||
echo ""
|
||||
echo "3. Open Web Dashboard"
|
||||
echo ""
|
||||
|
||||
docker run -it --rm -p 8080:8080 --link=optuna-postgres $OPTUNA_DASHBOARD_IMAGE "postgresql+psycopg2://optuna:password@optuna-postgres:5432/optuna"
|
||||
|
||||
echo ""
|
||||
echo "4. Stop MYSQL Server"
|
||||
echo ""
|
||||
|
||||
docker stop optuna-postgres
|
||||
@@ -1,19 +0,0 @@
|
||||
import sys
|
||||
|
||||
import optuna
|
||||
|
||||
|
||||
def objective(trial):
|
||||
x = trial.suggest_float("x", -100, 100)
|
||||
y = trial.suggest_categorical("y", [-1, 0, 1])
|
||||
return x**2 + y
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
if len(sys.argv) != 2:
|
||||
print("Invalid args")
|
||||
sys.exit(1)
|
||||
|
||||
db_url = sys.argv[1]
|
||||
study = optuna.create_study(storage=db_url, study_name="example-study")
|
||||
study.optimize(objective, n_trials=10)
|
||||
@@ -2,6 +2,7 @@ from __future__ import annotations
|
||||
|
||||
import csv
|
||||
import functools
|
||||
import importlib
|
||||
import io
|
||||
from itertools import chain
|
||||
import logging
|
||||
@@ -95,6 +96,7 @@ def create_app(
|
||||
def api_meta() -> dict[str, Any]:
|
||||
return {
|
||||
"artifact_is_available": artifact_store is not None,
|
||||
"plotlypy_is_available": importlib.util.find_spec("plotly") is not None,
|
||||
}
|
||||
|
||||
@app.get("/api/studies")
|
||||
@@ -261,6 +263,19 @@ def create_app(
|
||||
response.status = 400 # Bad request
|
||||
return {"reason": str(e)}
|
||||
|
||||
@app.get("/api/studies/<study_id:int>/plot/<plot_type>")
|
||||
@json_api_view
|
||||
def get_plot(study_id: int, plot_type: str) -> dict[str, Any]:
|
||||
study = optuna.load_study(
|
||||
study_name=storage.get_study_name_from_id(study_id), storage=storage
|
||||
)
|
||||
if plot_type == "contour":
|
||||
fig = optuna.visualization.plot_contour(study)
|
||||
else:
|
||||
response.status = 404 # Not found
|
||||
return {"reason": f"plot_type={plot_type} is not supported."}
|
||||
return fig.to_json()
|
||||
|
||||
@app.put("/api/studies/<study_id:int>/note")
|
||||
@json_api_view
|
||||
def save_study_note(study_id: int) -> dict[str, Any]:
|
||||
@@ -468,9 +483,9 @@ def create_app(
|
||||
param_names_header = [f"Param {x}" for x in param_names]
|
||||
user_attr_names_header = [f"UserAttribute {x}" for x in user_attr_names]
|
||||
n_objs = len(study.directions)
|
||||
if study.metric_names is not None:
|
||||
if hasattr(study, "metric_names") and study.metric_names is not None:
|
||||
value_header = study.metric_names
|
||||
else:
|
||||
else: # optuna < v3.4.0
|
||||
value_header = ["Value"] if n_objs == 1 else [f"Objective {x}" for x in range(n_objs)]
|
||||
column_names = (
|
||||
["Number", "State"] + value_header + param_names_header + user_attr_names_header
|
||||
|
||||
@@ -246,7 +246,7 @@ def serialize_frozen_trial(
|
||||
],
|
||||
"user_attrs": serialize_attrs(trial.user_attrs),
|
||||
"note": note.get_note_from_system_attrs(study_system_attrs, trial._trial_id),
|
||||
"artifacts": list_trial_artifacts(study_system_attrs, trial),
|
||||
"artifacts": list_trial_artifacts(study_system_attrs, trial_system_attrs, trial),
|
||||
"constraints": trial_system_attrs.get(CONSTRAINTS_KEY, []),
|
||||
}
|
||||
|
||||
|
||||
@@ -52,7 +52,18 @@ def get_artifact_path(
|
||||
study_or_trial: optuna.Trial | optuna.Study,
|
||||
artifact_id: str,
|
||||
) -> str:
|
||||
"""Get the URL path for a given artifact ID."""
|
||||
"""Get the URL path for a given artifact ID.
|
||||
|
||||
Args:
|
||||
study_or_trial:
|
||||
A Trial object, or a Study object.
|
||||
|
||||
artifact_id:
|
||||
An artifact ID.
|
||||
|
||||
Returns:
|
||||
A URL path to the artifact.
|
||||
"""
|
||||
if isinstance(study_or_trial, optuna.Study):
|
||||
study_id = study_or_trial._study_id
|
||||
return f"/artifacts/{study_id}/{artifact_id}"
|
||||
@@ -139,10 +150,12 @@ def register_artifact_route(
|
||||
storage.set_trial_system_attr(trial_id, attr_key, json.dumps(artifact))
|
||||
response.status = 201
|
||||
|
||||
trial = storage.get_trial(trial_id) # Fetch trial.system_attrs again.
|
||||
study_system_attrs = storage.get_study_system_attrs(study_id)
|
||||
trial_system_attrs = storage.get_trial_system_attrs(trial_id)
|
||||
artifacts = list_trial_artifacts(study_system_attrs, trial_system_attrs, trial)
|
||||
return {
|
||||
"artifact_id": artifact_id,
|
||||
"artifacts": list_trial_artifacts(storage.get_study_system_attrs(study_id), trial),
|
||||
"artifacts": artifacts,
|
||||
}
|
||||
|
||||
@app.post("/api/artifacts/<study_id:int>")
|
||||
@@ -322,7 +335,11 @@ def delete_all_artifacts(backend: ArtifactStore, storage: BaseStorage, study_id:
|
||||
study_system_attrs = storage.get_study_system_attrs(study_id)
|
||||
artifact_metas.extend(list_study_artifacts(study_system_attrs))
|
||||
for trial in storage.get_all_trials(study_id):
|
||||
trial_artifacts = list_trial_artifacts(study_system_attrs, trial)
|
||||
trial_system_attrs = getattr(trial, "_system_attrs")
|
||||
if trial_system_attrs is None:
|
||||
# This is unreachable line until Optuna v5.0.0 release.
|
||||
trial_system_attrs = storage.get_trial_system_attrs(trial._trial_id)
|
||||
trial_artifacts = list_trial_artifacts(study_system_attrs, trial_system_attrs, trial)
|
||||
artifact_metas.extend(trial_artifacts)
|
||||
|
||||
for meta in artifact_metas:
|
||||
@@ -339,7 +356,7 @@ def list_study_artifacts(study_system_attrs: dict[str, Any]) -> list[ArtifactMet
|
||||
|
||||
|
||||
def list_trial_artifacts(
|
||||
study_system_attrs: dict[str, Any], trial: FrozenTrial
|
||||
study_system_attrs: dict[str, Any], trial_system_attrs: dict[str, Any], trial: FrozenTrial
|
||||
) -> list[ArtifactMeta]:
|
||||
# Collect ArtifactMeta from study_system_attrs due to backward compatibility.
|
||||
dashboard_artifact_metas = [
|
||||
@@ -353,7 +370,7 @@ def list_trial_artifacts(
|
||||
# See https://github.com/optuna/optuna/blob/f827582a8/optuna/artifacts/_upload.py#L16
|
||||
optuna_artifact_metas = [
|
||||
json.loads(value)
|
||||
for key, value in trial.system_attrs.items()
|
||||
for key, value in trial_system_attrs.items()
|
||||
if key.startswith(ARTIFACTS_ATTR_PREFIX)
|
||||
]
|
||||
artifact_metas = dashboard_artifact_metas + optuna_artifact_metas
|
||||
|
||||
@@ -338,7 +338,8 @@ class PreferentialGPSampler(optuna.samplers.BaseSampler):
|
||||
trial: optuna.trial.FrozenTrial,
|
||||
search_space: dict[str, optuna.distributions.BaseDistribution],
|
||||
) -> dict[str, Any]:
|
||||
preferences = get_preferences(study.system_attrs)
|
||||
study_system_attrs = study._storage.get_study_system_attrs(study._study_id)
|
||||
preferences = get_preferences(study_system_attrs)
|
||||
if len(preferences) == 0 or len(search_space) == 0:
|
||||
return {}
|
||||
|
||||
|
||||
@@ -28,8 +28,10 @@ import {
|
||||
paramImportanceState,
|
||||
isFileUploading,
|
||||
artifactIsAvailable,
|
||||
plotlypyIsAvailableState,
|
||||
reloadIntervalState,
|
||||
trialsUpdatingState,
|
||||
studySummariesLoadingState,
|
||||
} from "./state"
|
||||
import { getDominatedTrials } from "./dominatedTrials"
|
||||
|
||||
@@ -46,6 +48,12 @@ export const actionCreator = () => {
|
||||
const setUploading = useSetRecoilState<boolean>(isFileUploading)
|
||||
const setTrialsUpdating = useSetRecoilState(trialsUpdatingState)
|
||||
const setArtifactIsAvailable = useSetRecoilState<boolean>(artifactIsAvailable)
|
||||
const setPlotlypyIsAvailable = useSetRecoilState<boolean>(
|
||||
plotlypyIsAvailableState
|
||||
)
|
||||
const setStudySummariesLoading = useSetRecoilState<boolean>(
|
||||
studySummariesLoadingState
|
||||
)
|
||||
|
||||
const setStudyDetailState = (studyId: number, study: StudyDetail) => {
|
||||
setStudyDetails((prevVal) => {
|
||||
@@ -211,12 +219,15 @@ export const actionCreator = () => {
|
||||
const updateAPIMeta = () => {
|
||||
getMetaInfoAPI().then((r) => {
|
||||
setArtifactIsAvailable(r.artifact_is_available)
|
||||
setPlotlypyIsAvailable(r.plotlypy_is_available)
|
||||
})
|
||||
}
|
||||
|
||||
const updateStudySummaries = (successMsg?: string) => {
|
||||
setStudySummariesLoading(true)
|
||||
getStudySummariesAPI()
|
||||
.then((studySummaries: StudySummary[]) => {
|
||||
setStudySummariesLoading(false)
|
||||
setStudySummaries(studySummaries)
|
||||
|
||||
if (successMsg) {
|
||||
@@ -224,6 +235,7 @@ export const actionCreator = () => {
|
||||
}
|
||||
})
|
||||
.catch((err) => {
|
||||
setStudySummariesLoading(false)
|
||||
enqueueSnackbar(`Failed to fetch study list.`, {
|
||||
variant: "error",
|
||||
})
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import * as plotly from "plotly.js-dist-min"
|
||||
import axios from "axios"
|
||||
|
||||
const axiosInstance = axios.create({ baseURL: API_ENDPOINT })
|
||||
|
||||
type APIMeta = {
|
||||
artifact_is_available: boolean
|
||||
plotlypy_is_available: boolean
|
||||
}
|
||||
|
||||
export const getMetaInfoAPI = (): Promise<APIMeta> => {
|
||||
@@ -439,3 +441,19 @@ export const reportFeedbackComponentAPI = (
|
||||
return
|
||||
})
|
||||
}
|
||||
|
||||
type PlotResponse = {
|
||||
data: plotly.Data[]
|
||||
layout: plotly.Layout
|
||||
}
|
||||
export enum PlotType {
|
||||
Contour = "contour",
|
||||
}
|
||||
export const getPlotAPI = (
|
||||
studyId: number,
|
||||
plotType: PlotType
|
||||
): Promise<PlotResponse> => {
|
||||
return axiosInstance
|
||||
.get<PlotResponse>(`/api/studies/${studyId}/plot/${plotType}`)
|
||||
.then<PlotResponse>((res) => res.data)
|
||||
}
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@ import {
|
||||
Button,
|
||||
DialogActions,
|
||||
} from "@mui/material"
|
||||
import { actionCreator } from "../action"
|
||||
import { actionCreator } from "../../action"
|
||||
|
||||
export const useDeleteTrialArtifactDialog = (): [
|
||||
(studyId: number, trialId: number, artifact: Artifact) => void,
|
||||
+1
-1
@@ -20,7 +20,7 @@ import DownloadIcon from "@mui/icons-material/Download"
|
||||
import DeleteIcon from "@mui/icons-material/Delete"
|
||||
import FullscreenIcon from "@mui/icons-material/Fullscreen"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { actionCreator } from "../../action"
|
||||
import { useDeleteStudyArtifactDialog } from "./DeleteArtifactDialog"
|
||||
import {
|
||||
useThreejsArtifactModal,
|
||||
+1
-1
@@ -115,7 +115,7 @@ export const ThreejsArtifactViewer: React.FC<ThreejsArtifactViewerProps> = (
|
||||
{geometry.length > 0 &&
|
||||
geometry.map((geo, index) => (
|
||||
<mesh key={index} geometry={geo}>
|
||||
<meshNormalMaterial />
|
||||
<meshNormalMaterial side={THREE.DoubleSide} />
|
||||
</mesh>
|
||||
))}
|
||||
</Canvas>
|
||||
+1
-1
@@ -20,7 +20,7 @@ import DownloadIcon from "@mui/icons-material/Download"
|
||||
import DeleteIcon from "@mui/icons-material/Delete"
|
||||
import FullscreenIcon from "@mui/icons-material/Fullscreen"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { actionCreator } from "../../action"
|
||||
import { useDeleteTrialArtifactDialog } from "./DeleteArtifactDialog"
|
||||
import {
|
||||
useThreejsArtifactModal,
|
||||
@@ -29,12 +29,8 @@ import { studySummariesState, studyDetailsState } from "../state"
|
||||
import { AppDrawer } from "./AppDrawer"
|
||||
import { GraphEdf } from "./GraphEdf"
|
||||
import { GraphHistory } from "./GraphHistory"
|
||||
import { useNavigate, useLocation } from "react-router-dom"
|
||||
|
||||
const useQuery = (): URLSearchParams => {
|
||||
const { search } = useLocation()
|
||||
return useMemo(() => new URLSearchParams(search), [search])
|
||||
}
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useQuery } from "../urlQuery"
|
||||
|
||||
const useQueriedStudies = (
|
||||
studies: StudySummary[],
|
||||
|
||||
@@ -8,10 +8,12 @@ import {
|
||||
TablePagination,
|
||||
TableRow,
|
||||
TableSortLabel,
|
||||
TextField,
|
||||
Collapse,
|
||||
IconButton,
|
||||
Menu,
|
||||
MenuItem,
|
||||
Box,
|
||||
} from "@mui/material"
|
||||
import { styled } from "@mui/system"
|
||||
import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"
|
||||
@@ -84,6 +86,41 @@ function DataGrid<T>(props: {
|
||||
setPage(0)
|
||||
}
|
||||
|
||||
const PaginationForm: React.FC<{
|
||||
onPageNumberSubmit: (value: number) => void
|
||||
maxPageNumber: number
|
||||
}> = ({ onPageNumberSubmit, maxPageNumber }) => {
|
||||
// This component is separated from DataGrid to prevent `DataGrid` from re-rendering the page,
|
||||
// every time any letters are input.
|
||||
const [specifiedPageText, setSpecifiedPageText] = React.useState("")
|
||||
|
||||
const handleSubmitPageNumber = (
|
||||
event: React.FormEvent<HTMLFormElement>
|
||||
) => {
|
||||
event.preventDefault()
|
||||
const newPageNumber = parseInt(specifiedPageText, 10)
|
||||
// Page is 0-indexed in `TablePagination`.
|
||||
onPageNumberSubmit(newPageNumber - 1)
|
||||
setSpecifiedPageText("") // reset the input field
|
||||
}
|
||||
|
||||
return (
|
||||
<form onSubmit={handleSubmitPageNumber}>
|
||||
<TextField
|
||||
size="small"
|
||||
label={`Go to Page: n / ${maxPageNumber}`}
|
||||
value={specifiedPageText}
|
||||
type="number"
|
||||
style={{ width: 200 }}
|
||||
inputProps={{ min: 1, max: maxPageNumber }}
|
||||
onChange={(e) => {
|
||||
setSpecifiedPageText(e.target.value)
|
||||
}}
|
||||
/>
|
||||
</form>
|
||||
)
|
||||
}
|
||||
|
||||
// Filtering
|
||||
const filteredRows = rows.filter((row, rowIdx) => {
|
||||
if (defaultFilter !== undefined && defaultFilter(row)) {
|
||||
@@ -119,6 +156,7 @@ function DataGrid<T>(props: {
|
||||
const RootDiv = styled("div")({
|
||||
width: "100%",
|
||||
})
|
||||
const maxPageNumber = Math.ceil(filteredRows.length / rowsPerPage)
|
||||
return (
|
||||
<RootDiv>
|
||||
<TableContainer>
|
||||
@@ -177,15 +215,28 @@ function DataGrid<T>(props: {
|
||||
</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
<TablePagination
|
||||
rowsPerPageOptions={rowsPerPageOption}
|
||||
component="div"
|
||||
count={filteredRows.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
{filteredRows.length > 0 ? (
|
||||
<>
|
||||
{/* @ts-ignore */}
|
||||
<Box display="flex" alignItems="center">
|
||||
<TablePagination
|
||||
rowsPerPageOptions={rowsPerPageOption}
|
||||
component="div"
|
||||
count={filteredRows.length}
|
||||
rowsPerPage={rowsPerPage}
|
||||
page={page}
|
||||
onPageChange={handleChangePage}
|
||||
onRowsPerPageChange={handleChangeRowsPerPage}
|
||||
/>
|
||||
{maxPageNumber > 2 ? (
|
||||
<PaginationForm
|
||||
onPageNumberSubmit={(page) => setPage(page)}
|
||||
maxPageNumber={maxPageNumber}
|
||||
/>
|
||||
) : null}
|
||||
</Box>
|
||||
</>
|
||||
) : null}
|
||||
</RootDiv>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -15,11 +15,53 @@ import blue from "@mui/material/colors/blue"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { usePlotlyColorTheme } from "../state"
|
||||
import { getAxisInfo } from "../graphUtil"
|
||||
import { useQuery } from "../urlQuery"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { plotlypyIsAvailableState } from "../state"
|
||||
|
||||
const plotDomId = "graph-contour"
|
||||
|
||||
export const Contour: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const query = useQuery()
|
||||
const plotlypyIsAvailable = useRecoilValue<boolean>(plotlypyIsAvailableState)
|
||||
if (query.get("plotlypy_rendering") === "true") {
|
||||
if (plotlypyIsAvailable) {
|
||||
return <ContourBackend study={study} />
|
||||
} else {
|
||||
console.warn(
|
||||
"Use frontend rendering because plotlypy is specified but not available."
|
||||
)
|
||||
return <ContourFrontend study={study} />
|
||||
}
|
||||
} else {
|
||||
return <ContourFrontend study={study} />
|
||||
}
|
||||
}
|
||||
|
||||
const ContourBackend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const studyId = study?.id
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.Contour)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId])
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
const ContourFrontend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const theme = useTheme()
|
||||
const colorTheme = usePlotlyColorTheme(theme.palette.mode)
|
||||
|
||||
@@ -60,6 +60,7 @@ const plotTimeline = (
|
||||
Running: "green",
|
||||
Waiting: "gray",
|
||||
}
|
||||
const runningKey = "Running"
|
||||
|
||||
const lastTrials = trials.slice(-maxBars) // To only show last elements
|
||||
const minDatetime = new Date(
|
||||
@@ -69,13 +70,38 @@ const plotTimeline = (
|
||||
)
|
||||
)
|
||||
)
|
||||
const maxDatetime = new Date(
|
||||
Math.max(
|
||||
...lastTrials.map(
|
||||
(t) => t.datetime_start?.getTime() ?? minDatetime.getTime()
|
||||
)
|
||||
)
|
||||
const maxRunDuration = Math.max(
|
||||
...trials.map((t) => {
|
||||
return t.datetime_start === undefined || t.datetime_complete === undefined
|
||||
? -Infinity
|
||||
: t.datetime_complete.getTime() - t.datetime_start.getTime()
|
||||
})
|
||||
)
|
||||
const hasRunning =
|
||||
(maxRunDuration === -Infinity &&
|
||||
trials.some((t) => t.state === runningKey)) ||
|
||||
trials.some((t) => {
|
||||
if (t.state !== runningKey) {
|
||||
return false
|
||||
}
|
||||
const now = new Date().getTime()
|
||||
const start = t.datetime_start?.getTime() ?? now
|
||||
// This is an ad-hoc handling to check if the trial is running.
|
||||
// We do not check via `trialState` because some trials may have state=RUNNING,
|
||||
// even if they are not running because of unexpected job kills.
|
||||
// In this case, we would like to ensure that these trials will not squash the timeline plot
|
||||
// for the other trials.
|
||||
return now - start < maxRunDuration * 5
|
||||
})
|
||||
const maxDatetime = hasRunning
|
||||
? new Date()
|
||||
: new Date(
|
||||
Math.max(
|
||||
...lastTrials.map(
|
||||
(t) => t.datetime_complete?.getTime() ?? minDatetime.getTime()
|
||||
)
|
||||
)
|
||||
)
|
||||
const layout: Partial<plotly.Layout> = {
|
||||
margin: {
|
||||
l: 50,
|
||||
@@ -97,11 +123,20 @@ const plotTimeline = (
|
||||
}
|
||||
|
||||
const makeTrace = (bars: Trial[], state: string, color: string) => {
|
||||
const starts = bars.map((b) => b.datetime_start ?? new Date())
|
||||
const completes = bars.map((b, i) => b.datetime_complete ?? starts[i])
|
||||
const isRunning = state === runningKey
|
||||
// Waiting trials should not squash other trials, so use `maxDatetime` instead of `new Date()`.
|
||||
const starts = bars.map((b) => b.datetime_start ?? maxDatetime)
|
||||
const runDurations = bars.map((b, i) => {
|
||||
const startTime = starts[i].getTime()
|
||||
const completeTime = isRunning
|
||||
? maxDatetime.getTime()
|
||||
: b.datetime_complete?.getTime() ?? startTime
|
||||
// By using 1 as the min value, we can recognize these bars at least when zooming in.
|
||||
return Math.max(1, completeTime - startTime)
|
||||
})
|
||||
const trace: Partial<plotly.PlotData> = {
|
||||
type: "bar",
|
||||
x: starts.map((s, i) => completes[i].getTime() - s.getTime()),
|
||||
x: runDurations,
|
||||
y: bars.map((b) => b.number),
|
||||
// @ts-ignore: To suppress ts(2322)
|
||||
base: starts.map((s) => s.toISOString()),
|
||||
|
||||
+4
-4
@@ -8,10 +8,10 @@ import {
|
||||
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"
|
||||
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()
|
||||
+1
-1
@@ -23,7 +23,7 @@ import "reactflow/dist/style.css"
|
||||
import ELK from "elkjs/lib/elk.bundled.js"
|
||||
import { ElkNode } from "elkjs/lib/elk-api.js"
|
||||
|
||||
import { useStudyDetailValue } from "../state"
|
||||
import { useStudyDetailValue } from "../../state"
|
||||
import { getArtifactUrlPath } from "./PreferentialTrials"
|
||||
import { PreferentialOutputComponent } from "./PreferentialOutputComponent"
|
||||
|
||||
+5
-5
@@ -15,11 +15,11 @@ import DeleteIcon from "@mui/icons-material/Delete"
|
||||
import Modal from "@mui/material/Modal"
|
||||
import { red } from "@mui/material/colors"
|
||||
|
||||
import { TrialListDetail } from "./TrialList"
|
||||
import { TrialListDetail } from "../TrialList"
|
||||
import { getArtifactUrlPath } from "./PreferentialTrials"
|
||||
import { formatDate } from "../dateUtil"
|
||||
import { actionCreator } from "../action"
|
||||
import { useStudyDetailValue } from "../state"
|
||||
import { formatDate } from "../../dateUtil"
|
||||
import { actionCreator } from "../../action"
|
||||
import { useStudyDetailValue } from "../../state"
|
||||
import { PreferentialOutputComponent } from "./PreferentialOutputComponent"
|
||||
|
||||
type TrialType = "worst" | "none"
|
||||
@@ -252,7 +252,7 @@ const ChoiceTrials: FC<{
|
||||
)
|
||||
}
|
||||
|
||||
export const PreferenceHistory: FC<{ studyDetail: StudyDetail | null }> = ({
|
||||
export const PreferentialHistory: FC<{ studyDetail: StudyDetail | null }> = ({
|
||||
studyDetail,
|
||||
}) => {
|
||||
if (
|
||||
+2
-2
@@ -1,6 +1,6 @@
|
||||
import React, { FC, useMemo } from "react"
|
||||
import { ArtifactCardMedia } from "./ArtifactCardMedia"
|
||||
import { MarkdownRenderer } from "./Note"
|
||||
import { ArtifactCardMedia } from "../Artifact/ArtifactCardMedia"
|
||||
import { MarkdownRenderer } from "../Note"
|
||||
|
||||
export const PreferentialOutputComponent: FC<{
|
||||
trial: Trial
|
||||
+3
-3
@@ -27,12 +27,12 @@ import ClearIcon from "@mui/icons-material/Clear"
|
||||
import SettingsIcon from "@mui/icons-material/Settings"
|
||||
import FullscreenIcon from "@mui/icons-material/Fullscreen"
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { TrialListDetail } from "./TrialList"
|
||||
import { actionCreator } from "../../action"
|
||||
import { TrialListDetail } from "../TrialList"
|
||||
import {
|
||||
isThreejsArtifact,
|
||||
useThreejsArtifactModal,
|
||||
} from "./ThreejsArtifactViewer"
|
||||
} from "../Artifact/ThreejsArtifactViewer"
|
||||
import { PreferentialOutputComponent } from "./PreferentialOutputComponent"
|
||||
|
||||
const SettingsPage: FC<{
|
||||
@@ -31,10 +31,10 @@ import { GraphEdf } from "./GraphEdf"
|
||||
import { GraphRank } from "./GraphRank"
|
||||
import { TrialList } from "./TrialList"
|
||||
import { StudyHistory } from "./StudyHistory"
|
||||
import { PreferentialTrials } from "./PreferentialTrials"
|
||||
import { PreferenceHistory } from "./PreferenceHistory"
|
||||
import { PreferentialAnalytics } from "./PreferentialAnalytics"
|
||||
import { PreferentialGraph } from "./PreferentialGraph"
|
||||
import { PreferentialTrials } from "./Preferential/PreferentialTrials"
|
||||
import { PreferentialHistory } from "./Preferential/PreferentialHistory"
|
||||
import { PreferentialAnalytics } from "./Preferential/PreferentialAnalytics"
|
||||
import { PreferentialGraph } from "./Preferential/PreferentialGraph"
|
||||
|
||||
interface ParamTypes {
|
||||
studyId: string
|
||||
@@ -221,7 +221,7 @@ export const StudyDetail: FC<{
|
||||
</Box>
|
||||
)
|
||||
} else if (page === "preferenceHistory") {
|
||||
content = <PreferenceHistory studyDetail={studyDetail} />
|
||||
content = <PreferentialHistory studyDetail={studyDetail} />
|
||||
}
|
||||
|
||||
const toolbar = (
|
||||
|
||||
@@ -17,7 +17,7 @@ import { DataGrid, DataGridColumn } from "./DataGrid"
|
||||
import { GraphHyperparameterImportance } from "./GraphHyperparameterImportances"
|
||||
import { UserDefinedPlot } from "./UserDefinedPlot"
|
||||
import { BestTrialsCard } from "./BestTrialsCard"
|
||||
import { StudyArtifactCards } from "./StudyArtifactCards"
|
||||
import { StudyArtifactCards } from "./Artifact/StudyArtifactCards"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import {
|
||||
useStudyDetailValue,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import React, { FC, useEffect, useMemo, useState } from "react"
|
||||
import { useNavigate, useLocation } from "react-router-dom"
|
||||
import React, { FC, useEffect, useState } from "react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { Link } from "react-router-dom"
|
||||
import {
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TextField,
|
||||
CardActions,
|
||||
} from "@mui/material"
|
||||
import { Delete, Refresh, Search } from "@mui/icons-material"
|
||||
import { Delete, Refresh, Search, HourglassTop } from "@mui/icons-material"
|
||||
import SortIcon from "@mui/icons-material/Sort"
|
||||
import HomeIcon from "@mui/icons-material/Home"
|
||||
import AddBoxIcon from "@mui/icons-material/AddBox"
|
||||
@@ -27,12 +27,13 @@ import DriveFileRenameOutlineIcon from "@mui/icons-material/DriveFileRenameOutli
|
||||
|
||||
import { actionCreator } from "../action"
|
||||
import { DebouncedInputTextField } from "./Debounce"
|
||||
import { studySummariesState } from "../state"
|
||||
import { studySummariesLoadingState, studySummariesState } from "../state"
|
||||
import { styled } from "@mui/system"
|
||||
import { AppDrawer } from "./AppDrawer"
|
||||
import { useCreateStudyDialog } from "./CreateStudyDialog"
|
||||
import { useDeleteStudyDialog } from "./DeleteStudyDialog"
|
||||
import { useRenameStudyDialog } from "./RenameStudyDialog"
|
||||
import { useQuery } from "../urlQuery"
|
||||
|
||||
export const StudyList: FC<{
|
||||
toggleColorMode: () => void
|
||||
@@ -57,12 +58,9 @@ export const StudyList: FC<{
|
||||
useDeleteStudyDialog()
|
||||
const [openRenameStudyDialog, renderRenameStudyDialog] =
|
||||
useRenameStudyDialog(studies)
|
||||
const isLoading = useRecoilValue<boolean>(studySummariesLoadingState)
|
||||
|
||||
const navigate = useNavigate()
|
||||
const useQuery = (): URLSearchParams => {
|
||||
const { search } = useLocation()
|
||||
return useMemo(() => new URLSearchParams(search), [search])
|
||||
}
|
||||
const query = useQuery()
|
||||
const initialSortBy = query.get("studies_order_by") === "asc" ? "asc" : "desc"
|
||||
const [sortBy, setSortBy] = useState<"asc" | "desc">(initialSortBy)
|
||||
@@ -72,7 +70,6 @@ export const StudyList: FC<{
|
||||
if (sortBy === "desc") {
|
||||
filteredStudies = filteredStudies.reverse()
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
action.updateStudySummaries()
|
||||
}, [])
|
||||
@@ -126,6 +123,71 @@ export const StudyList: FC<{
|
||||
|
||||
const toolbar = <HomeIcon sx={{ margin: theme.spacing(0, 1) }} />
|
||||
|
||||
let studyListContent
|
||||
if (isLoading) {
|
||||
studyListContent = (
|
||||
<Box sx={{ margin: theme.spacing(2) }}>
|
||||
<SvgIcon fontSize="small" color="action">
|
||||
<HourglassTop />
|
||||
</SvgIcon>
|
||||
Loading studies...
|
||||
</Box>
|
||||
)
|
||||
} else {
|
||||
studyListContent = filteredStudies.map((study) => (
|
||||
<Card
|
||||
key={study.study_id}
|
||||
sx={{ margin: theme.spacing(2), width: "500px" }}
|
||||
>
|
||||
<CardActionArea
|
||||
component={Link}
|
||||
to={`${URL_PREFIX}/studies/${study.study_id}`}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography variant="h5">
|
||||
{study.study_id}. {study.study_name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
color="text.secondary"
|
||||
component="div"
|
||||
>
|
||||
{study.is_preferential
|
||||
? "Preferential Optimization"
|
||||
: "Direction: " +
|
||||
study.directions
|
||||
.map((d) => d.toString().toUpperCase())
|
||||
.join(", ")}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<CardActions disableSpacing sx={{ paddingTop: 0 }}>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
<IconButton
|
||||
aria-label="rename study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
openRenameStudyDialog(study.study_id, study.study_name)
|
||||
}}
|
||||
>
|
||||
<DriveFileRenameOutlineIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="delete study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
openDeleteStudyDialog(study.study_id)
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</CardActions>
|
||||
</Card>
|
||||
))
|
||||
}
|
||||
|
||||
return (
|
||||
<Box sx={{ display: "flex" }}>
|
||||
<AppDrawer toggleColorMode={toggleColorMode} toolbar={toolbar}>
|
||||
@@ -196,58 +258,7 @@ export const StudyList: FC<{
|
||||
</CardContent>
|
||||
</Card>
|
||||
<Box sx={{ display: "flex", flexWrap: "wrap" }}>
|
||||
{filteredStudies.map((study) => (
|
||||
<Card
|
||||
key={study.study_id}
|
||||
sx={{ margin: theme.spacing(2), width: "500px" }}
|
||||
>
|
||||
<CardActionArea
|
||||
component={Link}
|
||||
to={`${URL_PREFIX}/studies/${study.study_id}`}
|
||||
>
|
||||
<CardContent>
|
||||
<Typography variant="h5">
|
||||
{study.study_id}. {study.study_name}
|
||||
</Typography>
|
||||
<Typography
|
||||
variant="subtitle1"
|
||||
color="text.secondary"
|
||||
component="div"
|
||||
>
|
||||
{study.is_preferential
|
||||
? "Preferential Optimization"
|
||||
: "Direction: " +
|
||||
study.directions
|
||||
.map((d) => d.toString().toUpperCase())
|
||||
.join(", ")}
|
||||
</Typography>
|
||||
</CardContent>
|
||||
</CardActionArea>
|
||||
<CardActions disableSpacing sx={{ paddingTop: 0 }}>
|
||||
<Box sx={{ flexGrow: 1 }} />
|
||||
<IconButton
|
||||
aria-label="rename study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
openRenameStudyDialog(study.study_id, study.study_name)
|
||||
}}
|
||||
>
|
||||
<DriveFileRenameOutlineIcon />
|
||||
</IconButton>
|
||||
<IconButton
|
||||
aria-label="delete study"
|
||||
size="small"
|
||||
color="inherit"
|
||||
onClick={() => {
|
||||
openDeleteStudyDialog(study.study_id)
|
||||
}}
|
||||
>
|
||||
<Delete />
|
||||
</IconButton>
|
||||
</CardActions>
|
||||
</Card>
|
||||
))}
|
||||
{studyListContent}
|
||||
</Box>
|
||||
</Container>
|
||||
</AppDrawer>
|
||||
|
||||
@@ -21,13 +21,14 @@ import CheckBoxIcon from "@mui/icons-material/CheckBox"
|
||||
import StopCircleIcon from "@mui/icons-material/StopCircle"
|
||||
|
||||
import { TrialNote } from "./Note"
|
||||
import { useNavigate, useLocation } from "react-router-dom"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import ListItemIcon from "@mui/material/ListItemIcon"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { artifactIsAvailable } from "../state"
|
||||
import { actionCreator } from "../action"
|
||||
import { TrialFormWidgets } from "./TrialFormWidgets"
|
||||
import { TrialArtifactCards } from "./TrialArtifactCards"
|
||||
import { TrialArtifactCards } from "./Artifact/TrialArtifactCards"
|
||||
import { useQuery } from "../urlQuery"
|
||||
|
||||
const states: TrialState[] = [
|
||||
"Complete",
|
||||
@@ -61,12 +62,6 @@ const getChipColor = (state: TrialState): Color => {
|
||||
return "default"
|
||||
}
|
||||
|
||||
const useQuery = (): URLSearchParams => {
|
||||
const { search } = useLocation()
|
||||
|
||||
return useMemo(() => new URLSearchParams(search), [search])
|
||||
}
|
||||
|
||||
const useExcludedStates = (query: URLSearchParams): TrialState[] => {
|
||||
return useMemo(() => {
|
||||
const exclude = query.get("exclude")
|
||||
|
||||
@@ -55,6 +55,16 @@ export const plotlyColorTheme = atom<PlotlyColorTheme>({
|
||||
},
|
||||
})
|
||||
|
||||
export const plotlypyIsAvailableState = atom<boolean>({
|
||||
key: "plotlypyIsAvailable",
|
||||
default: true,
|
||||
})
|
||||
|
||||
export const studySummariesLoadingState = atom<boolean>({
|
||||
key: "studySummariesLoadingState",
|
||||
default: false,
|
||||
})
|
||||
|
||||
export const useStudyDetailValue = (studyId: number): StudyDetail | null => {
|
||||
const studyDetails = useRecoilValue<StudyDetails>(studyDetailsState)
|
||||
return studyDetails[studyId] || null
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
import { useLocation } from "react-router-dom"
|
||||
import { useMemo } from "react"
|
||||
|
||||
export const useQuery = (): URLSearchParams => {
|
||||
const { search } = useLocation()
|
||||
return useMemo(() => new URLSearchParams(search), [search])
|
||||
}
|
||||
Generated
+8343
-7938
File diff suppressed because it is too large
Load Diff
+43
-43
@@ -18,56 +18,56 @@
|
||||
"author": "Masashi Shibata",
|
||||
"license": "MIT",
|
||||
"dependencies": {
|
||||
"@emotion/react": "^11.10.8",
|
||||
"@emotion/styled": "^11.10.8",
|
||||
"@mui/icons-material": "^5.11.6",
|
||||
"@mui/lab": "^5.0.0-alpha.128",
|
||||
"@mui/material": "^5.12.1",
|
||||
"@react-three/drei": "^9.80.0",
|
||||
"@react-three/fiber": "^8.13.6",
|
||||
"@types/three": "^0.154.0",
|
||||
"axios": "^1.6.0",
|
||||
"elkjs": "^0.8.2",
|
||||
"@emotion/react": "^11.11.3",
|
||||
"@emotion/styled": "^11.11.0",
|
||||
"@mui/icons-material": "^5.15.6",
|
||||
"@mui/lab": "^5.0.0-alpha.162",
|
||||
"@mui/material": "^5.15.6",
|
||||
"@react-three/drei": "^9.96.4",
|
||||
"@react-three/fiber": "^8.15.15",
|
||||
"@types/three": "^0.160.0",
|
||||
"axios": "^1.6.7",
|
||||
"elkjs": "^0.9.1",
|
||||
"notistack": "^3.0.1",
|
||||
"plotly.js-dist-min": "^2.22.0",
|
||||
"plotly.js-dist-min": "^2.28.0",
|
||||
"react": "^18.2.0",
|
||||
"react-dom": "^18.2.0",
|
||||
"react-markdown": "^8.0.4",
|
||||
"react-router-dom": "^6.11.0",
|
||||
"react-markdown": "^9.0.1",
|
||||
"react-router-dom": "^6.21.3",
|
||||
"react-syntax-highlighter": "^15.5.0",
|
||||
"reactflow": "^11.8.3",
|
||||
"reactflow": "^11.10.3",
|
||||
"recoil": "^0.7.7",
|
||||
"rehype-mathjax": "^4.0.2",
|
||||
"rehype-raw": "^6.1.1",
|
||||
"remark-gfm": "^3.0.1",
|
||||
"remark-math": "^5.1.1",
|
||||
"three": "^0.155.0",
|
||||
"wavesurfer.js": "^7.4.12"
|
||||
"rehype-mathjax": "^6.0.0",
|
||||
"rehype-raw": "^7.0.0",
|
||||
"remark-gfm": "^4.0.0",
|
||||
"remark-math": "^6.0.0",
|
||||
"three": "^0.160.1",
|
||||
"wavesurfer.js": "^7.7.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.14.3",
|
||||
"@babel/preset-env": "^7.14.5",
|
||||
"@testing-library/react": "^13.4.0",
|
||||
"@types/jest": "^29.2.1",
|
||||
"@types/plotly.js": "^2.12.11",
|
||||
"@types/react": "^18.0.26",
|
||||
"@types/react-dom": "^18.0.10",
|
||||
"@types/react-syntax-highlighter": "^15.5.5",
|
||||
"@typescript-eslint/eslint-plugin": "^6.10.0",
|
||||
"@typescript-eslint/parser": "^6.10.0",
|
||||
"compression-webpack-plugin": "^10.0.0",
|
||||
"css-loader": "^6.8.1",
|
||||
"esbuild-loader": "^2.18.0",
|
||||
"eslint": "^8.53.0",
|
||||
"jest": "^29.2.1",
|
||||
"jest-canvas-mock": "^2.3.1",
|
||||
"jest-environment-jsdom": "^29.3.1",
|
||||
"@babel/core": "^7.23.9",
|
||||
"@babel/preset-env": "^7.23.9",
|
||||
"@testing-library/react": "^14.1.2",
|
||||
"@types/jest": "^29.5.11",
|
||||
"@types/plotly.js": "^2.12.32",
|
||||
"@types/react": "^18.2.48",
|
||||
"@types/react-dom": "^18.2.18",
|
||||
"@types/react-syntax-highlighter": "^15.5.11",
|
||||
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
||||
"@typescript-eslint/parser": "^6.19.1",
|
||||
"compression-webpack-plugin": "^11.0.0",
|
||||
"css-loader": "^6.9.1",
|
||||
"esbuild-loader": "^4.0.3",
|
||||
"eslint": "^8.56.0",
|
||||
"jest": "^29.7.0",
|
||||
"jest-canvas-mock": "^2.5.2",
|
||||
"jest-environment-jsdom": "^29.7.0",
|
||||
"prettier": "^2.5.1",
|
||||
"style-loader": "^3.3.3",
|
||||
"ts-jest": "^29.0.3",
|
||||
"ts-loader": "^9.2.7",
|
||||
"typescript": "^4.6.2",
|
||||
"webpack": "^5.76.0",
|
||||
"webpack-cli": "^4.9.2"
|
||||
"style-loader": "^3.3.4",
|
||||
"ts-jest": "^29.1.2",
|
||||
"ts-loader": "^9.5.1",
|
||||
"typescript": "^5.3.3",
|
||||
"webpack": "^5.90.0",
|
||||
"webpack-cli": "^5.1.4"
|
||||
}
|
||||
}
|
||||
|
||||
+5
-1
@@ -27,7 +27,7 @@ classifiers = [
|
||||
]
|
||||
dependencies = [
|
||||
"bottle",
|
||||
"optuna>=2.4.0",
|
||||
"optuna>=3.1.0",
|
||||
"packaging",
|
||||
"scikit-learn",
|
||||
]
|
||||
@@ -59,6 +59,10 @@ preferential = [
|
||||
"botorch>=0.8.1",
|
||||
]
|
||||
|
||||
plotlypy = [
|
||||
"plotly",
|
||||
]
|
||||
|
||||
[project.scripts]
|
||||
optuna-dashboard = "optuna_dashboard._cli:main"
|
||||
|
||||
|
||||
@@ -4,6 +4,13 @@ import tempfile
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import optuna
|
||||
from packaging import version
|
||||
import pytest
|
||||
|
||||
|
||||
if version.parse(optuna.__version__) < version.parse("3.3.0"):
|
||||
pytest.skip("optuna.artiracts module is introduced at v3.3.0", allow_module_level=True)
|
||||
|
||||
from optuna.artifacts import FileSystemArtifactStore
|
||||
from optuna.artifacts import upload_artifact
|
||||
from optuna.storages import BaseStorage
|
||||
@@ -12,7 +19,6 @@ from optuna_dashboard.artifact import _backend
|
||||
from optuna_dashboard.artifact import upload_artifact as dashboard_upload_artifact
|
||||
from optuna_dashboard.artifact._backend_to_store import to_artifact_store
|
||||
from optuna_dashboard.artifact.file_system import FileSystemBackend
|
||||
import pytest
|
||||
|
||||
from ..wsgi_client import send_request
|
||||
|
||||
@@ -84,9 +90,12 @@ def test_delete_all_artifacts(init_storage_with_artifact_meta: MagicMock) -> Non
|
||||
|
||||
def test_list_trial_artifacts(init_storage_with_artifact_meta: MagicMock) -> None:
|
||||
storage = init_storage_with_artifact_meta
|
||||
trial = MagicMock(_trial_id=0, system_attrs=storage.get_trial_system_attrs(0))
|
||||
trial_system_attrs = storage.get_trial_system_attrs(0)
|
||||
trial = MagicMock(_trial_id=0, system_attrs={})
|
||||
|
||||
actual = _backend.list_trial_artifacts(storage.get_study_system_attrs(0), trial)
|
||||
actual = _backend.list_trial_artifacts(
|
||||
storage.get_study_system_attrs(0), trial_system_attrs, trial
|
||||
)
|
||||
assert actual == [
|
||||
{"artifact_id": "id0", "filename": "foo.txt"},
|
||||
{"artifact_id": "id1", "filename": "bar.txt"},
|
||||
@@ -119,6 +128,10 @@ def test_study_artifact_not_found() -> None:
|
||||
assert status == 404
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.4.0"),
|
||||
reason="upload_artiract needs storage",
|
||||
)
|
||||
def test_successful_study_artifact_retrieval() -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = optuna.create_study(storage=storage)
|
||||
@@ -254,6 +267,10 @@ def test_upload_artifact() -> None:
|
||||
assert data == "dummy_content"
|
||||
|
||||
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.4.0"),
|
||||
reason="upload_artiract needs storage",
|
||||
)
|
||||
def test_delete_study_artifact() -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = optuna.create_study(storage=storage)
|
||||
|
||||
@@ -2,12 +2,17 @@ import io
|
||||
from unittest import TestCase
|
||||
|
||||
import boto3
|
||||
from moto import mock_s3
|
||||
from optuna_dashboard.artifact.boto3 import Boto3Backend
|
||||
from optuna_dashboard.artifact.exceptions import ArtifactNotFound
|
||||
|
||||
|
||||
@mock_s3
|
||||
try:
|
||||
from moto import mock_aws
|
||||
except ImportError:
|
||||
from moto import mock_s3 as mock_aws
|
||||
|
||||
|
||||
@mock_aws
|
||||
class Boto3BackendTestCase(TestCase):
|
||||
def setUp(self) -> None:
|
||||
self.s3_client = boto3.client("s3")
|
||||
|
||||
@@ -36,8 +36,11 @@ def test_list_optuna_trial_artifacts() -> None:
|
||||
study.tell(trial, 0.0)
|
||||
|
||||
study_system_attrs = storage.get_study_system_attrs(study._study_id)
|
||||
trial_system_attrs = storage.get_trial_system_attrs(trial._trial_id)
|
||||
frozen_trial = storage.get_trial(trial._trial_id)
|
||||
artifact_meta_list = list_trial_artifacts(study_system_attrs, frozen_trial)
|
||||
artifact_meta_list = list_trial_artifacts(
|
||||
study_system_attrs, trial_system_attrs, frozen_trial
|
||||
)
|
||||
assert len(artifact_meta_list) == 1
|
||||
|
||||
artifact_id = artifact_meta_list[0]["artifact_id"]
|
||||
|
||||
@@ -11,6 +11,7 @@ from optuna.distributions import IntDistribution
|
||||
from optuna.samplers import BaseSampler
|
||||
from optuna.trial import TrialState
|
||||
from optuna_dashboard.preferential import create_study
|
||||
from packaging import version
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -27,7 +28,9 @@ parametrize_sampler = pytest.mark.parametrize(
|
||||
pytest.param(
|
||||
PreferentialGPSampler,
|
||||
marks=pytest.mark.skipif(
|
||||
sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support"
|
||||
sys.version_info < (3, 8)
|
||||
or version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="BoTorch dropped Python3.7 support",
|
||||
),
|
||||
),
|
||||
],
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import Callable
|
||||
from unittest.mock import patch
|
||||
import uuid
|
||||
|
||||
import optuna
|
||||
from optuna import copy_study
|
||||
from optuna import create_trial
|
||||
from optuna import delete_study
|
||||
@@ -17,12 +18,16 @@ from optuna.exceptions import DuplicatedStudyError
|
||||
from optuna.trial import TrialState
|
||||
from optuna_dashboard.preferential import create_study
|
||||
from optuna_dashboard.preferential import load_study
|
||||
from packaging import version
|
||||
import pytest
|
||||
|
||||
from ..storage_supplier import parametrize_storages
|
||||
from ..storage_supplier import StorageSupplier
|
||||
|
||||
|
||||
if version.parse(optuna.__version__) < version.parse("3.4.0"):
|
||||
pytest.skip("Preferential optimization is introduced at v3.4.0", allow_module_level=True)
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
pytest.skip("BoTorch dropped Python3.7 support", allow_module_level=True)
|
||||
|
||||
|
||||
@@ -17,6 +17,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
|
||||
from packaging import version
|
||||
import pytest
|
||||
|
||||
from .wsgi_client import send_request
|
||||
@@ -110,6 +111,10 @@ class APITestCase(TestCase):
|
||||
self.assertEqual(status, 400)
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_get_best_trials_of_preferential_study(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(n_generate=4, storage=storage)
|
||||
@@ -134,6 +139,10 @@ class APITestCase(TestCase):
|
||||
assert best_trials[0]["number"] == 0
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_report_preference(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(n_generate=4, storage=storage)
|
||||
@@ -168,6 +177,10 @@ class APITestCase(TestCase):
|
||||
assert worse.number == 1
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_report_preference_when_typo_mode(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(storage=storage, n_generate=3)
|
||||
@@ -192,6 +205,10 @@ class APITestCase(TestCase):
|
||||
self.assertEqual(status, 400)
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_change_component(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(storage=storage, n_generate=3)
|
||||
@@ -327,6 +344,10 @@ class APITestCase(TestCase):
|
||||
assert note_ver_key(0) not in study.system_attrs
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_skip_trial(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(n_generate=4, storage=storage)
|
||||
@@ -352,6 +373,10 @@ class APITestCase(TestCase):
|
||||
assert best_trials[0].number == 2
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_remove_history(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(storage=storage, n_generate=3)
|
||||
@@ -386,6 +411,10 @@ class APITestCase(TestCase):
|
||||
assert len(study.get_preferences()) == 0
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"),
|
||||
reason="Needs optuna.search_space",
|
||||
)
|
||||
def test_restore_history(self) -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(storage=storage, n_generate=3)
|
||||
|
||||
@@ -5,6 +5,7 @@ import sys
|
||||
from typing import Callable
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
import optuna
|
||||
from optuna.storages import BaseStorage
|
||||
from optuna_dashboard._preferential_history import _SYSTEM_ATTR_PREFIX_HISTORY
|
||||
from optuna_dashboard._preferential_history import NewHistory
|
||||
@@ -14,6 +15,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
|
||||
from packaging import version
|
||||
import pytest
|
||||
|
||||
from .storage_supplier import parametrize_storages
|
||||
@@ -24,6 +26,10 @@ if TYPE_CHECKING:
|
||||
from optuna_dashboard._preferential_history import History
|
||||
|
||||
|
||||
if version.parse(optuna.__version__) < version.parse("3.4.0"):
|
||||
pytest.skip("Preferential optimization is introduced at v3.4.0", allow_module_level=True)
|
||||
|
||||
|
||||
if sys.version_info < (3, 8):
|
||||
pytest.skip("BoTorch dropped Python3.7 support", allow_module_level=True)
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@ 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
|
||||
from packaging import version
|
||||
import pytest
|
||||
|
||||
|
||||
@@ -53,6 +54,9 @@ def test_serialize_numpy_floating() -> None:
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"), reason="Needs optuna.search_space"
|
||||
)
|
||||
def test_get_study_detail_is_preferential() -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
study = create_study(n_generate=4, storage=storage)
|
||||
@@ -80,6 +84,9 @@ def test_get_study_detail_is_not_preferential() -> None:
|
||||
|
||||
|
||||
@pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support")
|
||||
@pytest.mark.skipif(
|
||||
version.parse(optuna.__version__) < version.parse("3.2.0"), reason="Needs optuna.search_space"
|
||||
)
|
||||
def test_get_study_summary_is_preferential() -> None:
|
||||
storage = optuna.storages.InMemoryStorage()
|
||||
create_study(n_generate=4, storage=storage)
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
# project dependency
|
||||
optuna>=2.4
|
||||
optuna>=3.1
|
||||
bottle
|
||||
scikit-learn
|
||||
streamlit
|
||||
|
||||
Reference in New Issue
Block a user