From 679646bacf7a949269d0f16a49de305201597e25 Mon Sep 17 00:00:00 2001 From: RuTiO2le Date: Sat, 30 Sep 2023 17:47:35 +0900 Subject: [PATCH 01/99] add csv download --- optuna_dashboard/_app.py | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 812201ad..7f263dfd 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -1,6 +1,8 @@ from __future__ import annotations +import csv import functools +import io import logging import os import typing @@ -447,6 +449,41 @@ def create_app( note.save_note_with_version(storage, study_id, trial_id, req_note_ver, req_note_body) response.status = 204 # No content return {} + + @app.get("/csv/") + def download_csv(study_id: int) -> BottleViewReturn: + # TODO: Create a CSV file + summary = get_study_summary(storage, study_id) + if summary is None: + response.status = 404 # Not found + return {"reason": f"study_id={study_id} is not found"} + trials = get_trials(storage, study_id) + + param_names = list(trials[0].params.keys()) + union_user_attrs = list(trials[0].user_attrs) + column_names = ["Number", "State", "Value"] + param_names + union_user_attrs + + buf = io.StringIO("") + writer = csv.writer(buf) + writer.writerow(column_names) + for frozen_trial in trials: + row = [ + frozen_trial.number, + frozen_trial.state, + frozen_trial.values[0] + ] + row += [frozen_trial.params[param] for param in param_names] + row += [frozen_trial.user_attrs[attr] for attr in union_user_attrs] + writer.writerow(row) + + # TODO: Set response headers + response.headers["Content-Type"] = "text/csv; chatset=cp932" + response.headers["Content-Disposition"] = f"attachment; filename=trials_{study_id}.csv" + + # TODO: Response body + buf.seek(0) + return buf.read() + @app.get("/favicon.ico") def favicon() -> BottleViewReturn: From c6f495dfa694720ba3edb5d81298249bb0556d51 Mon Sep 17 00:00:00 2001 From: RuTiO2le Date: Fri, 6 Oct 2023 06:22:46 +0900 Subject: [PATCH 02/99] add csv download button --- optuna_dashboard/ts/components/StudyDetail.tsx | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index ab37d02b..dc7d5894 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -12,6 +12,7 @@ import { import Grid2 from "@mui/material/Unstable_Grid2" import ChevronRightIcon from "@mui/icons-material/ChevronRight" import HomeIcon from "@mui/icons-material/Home" +import DownloadIcon from "@mui/icons-material/Download" import { StudyNote } from "./Note" import { actionCreator } from "../action" @@ -143,6 +144,16 @@ export const StudyDetail: FC<{ content = ( + + + From 44ad9489829ed3adee2a5cc89ab1e026906b9a14 Mon Sep 17 00:00:00 2001 From: RuTiO2le Date: Fri, 6 Oct 2023 06:54:02 +0900 Subject: [PATCH 03/99] remove branks in L484 --- optuna_dashboard/_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 7f263dfd..0a84c7c5 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -481,7 +481,7 @@ def create_app( response.headers["Content-Disposition"] = f"attachment; filename=trials_{study_id}.csv" # TODO: Response body - buf.seek(0) + buf.seek(0) return buf.read() From 933070b8a1e120f88d7c48165b0ef6f37214eabf Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 20 Oct 2023 17:19:52 +0900 Subject: [PATCH 04/99] Implement uplead study artifact ui --- optuna_dashboard/artifact/_backend.py | 35 ++- optuna_dashboard/ts/action.ts | 41 +++- optuna_dashboard/ts/apiClient.ts | 7 +- optuna_dashboard/ts/components/Note.tsx | 4 +- .../ts/components/StudyArtifactCards.tsx | 232 ++++++++++++++++++ .../ts/components/StudyHistory.tsx | 14 ++ .../ts/components/TrialArtifactCards.tsx | 4 +- 7 files changed, 328 insertions(+), 9 deletions(-) create mode 100644 optuna_dashboard/ts/components/StudyArtifactCards.tsx diff --git a/optuna_dashboard/artifact/_backend.py b/optuna_dashboard/artifact/_backend.py index 10a5b1ee..ee1b85a8 100644 --- a/optuna_dashboard/artifact/_backend.py +++ b/optuna_dashboard/artifact/_backend.py @@ -105,7 +105,7 @@ def register_artifact_route( @app.post("/api/artifacts//") @json_api_view - def upload_artifact_api(study_id: int, trial_id: int) -> dict[str, Any]: + def upload_trial_artifact_api(study_id: int, trial_id: int) -> dict[str, Any]: trial = storage.get_trial(trial_id) if trial is None: response.status = 400 @@ -144,6 +144,39 @@ def register_artifact_route( "artifacts": list_trial_artifacts(storage.get_study_system_attrs(study_id), trial), } + @app.post("/api/artifacts/") + @json_api_view + def upload_study_artifact_api(study_id: int) -> dict[str, Any]: + if artifact_store is None: + response.status = 400 # Bad Request + return {"reason": "Cannot access to the artifacts."} + file = request.json.get("file") + if file is None: + response.status = 400 + return {"reason": "Please specify the 'file' key."} + + _, data = parse_data_uri(file) + filename = request.json.get("filename", "") + artifact_id = str(uuid.uuid4()) + artifact_store.write(artifact_id, io.BytesIO(data)) + + mimetype, encoding = mimetypes.guess_type(filename) + artifact = { + "artifact_id": artifact_id, + "filename": filename, + "mimetype": mimetype or DEFAULT_MIME_TYPE, + "encoding": encoding, + } + attr_key = ARTIFACTS_ATTR_PREFIX + artifact_id + storage.set_study_system_attr(study_id, attr_key, json.dumps(artifact)) + + response.status = 201 + + return { + "artifact_id": artifact_id, + "artifacts": list_study_artifacts(storage.get_study_system_attrs(study_id)), + } + @app.delete("/api/artifacts///") @json_api_view def delete_artifact(study_id: int, trial_id: int, artifact_id: str) -> dict[str, Any]: diff --git a/optuna_dashboard/ts/action.ts b/optuna_dashboard/ts/action.ts index 41afdadb..cfcf4c24 100644 --- a/optuna_dashboard/ts/action.ts +++ b/optuna_dashboard/ts/action.ts @@ -100,6 +100,12 @@ export const actionCreator = () => { setTrial(studyId, trialIndex, newTrial) } + const setStudyArtifacts = (studyId: number, artifacts: Artifact[]) => { + const newStudy: StudyDetail = Object.assign({}, studyDetails[studyId]) + newStudy.artifacts = artifacts + setStudyDetailState(studyId, newStudy) + } + const deleteTrialArtifact = ( studyId: number, trialId: number, @@ -430,7 +436,7 @@ export const actionCreator = () => { }) } - const uploadArtifact = ( + const uploadTrialArtifact = ( studyId: number, trialId: number, file: File @@ -467,6 +473,36 @@ export const actionCreator = () => { } } + const uploadStudyArtifact = ( + studyId: number, + file: File + ): void => { + const reader = new FileReader() + setUploading(true) + reader.readAsDataURL(file) + reader.onload = (upload: ProgressEvent) => { + uploadArtifactAPI( + studyId, + null, + file.name, + upload.target?.result as string + ) + .then((res) => { + setUploading(false) + setStudyArtifacts(studyId, res.artifacts) + }) + .catch((err) => { + setUploading(false) + const reason = err.response?.data.reason + enqueueSnackbar(`Failed to upload ${reason}`, { variant: "error" }) + }) + } + reader.onerror = (error) => { + enqueueSnackbar(`Failed to read the file ${error}`, { variant: "error" }) + console.log(error) + } + } + const deleteArtifact = ( studyId: number, trialId: number, @@ -693,7 +729,8 @@ export const actionCreator = () => { saveReloadInterval, saveStudyNote, saveTrialNote, - uploadArtifact, + uploadTrialArtifact, + uploadStudyArtifact, deleteArtifact, makeTrialComplete, makeTrialFail, diff --git a/optuna_dashboard/ts/apiClient.ts b/optuna_dashboard/ts/apiClient.ts index e5510d67..c1dedd14 100644 --- a/optuna_dashboard/ts/apiClient.ts +++ b/optuna_dashboard/ts/apiClient.ts @@ -282,12 +282,15 @@ type UploadArtifactAPIResponse = { export const uploadArtifactAPI = ( studyId: number, - trialId: number, + trialId: number | null, fileName: string, dataUrl: string ): Promise => { + const APIurl = `/api/artifacts/${studyId}${ + trialId != null ? `/${trialId}` : "" + }` return axiosInstance - .post(`/api/artifacts/${studyId}/${trialId}`, { + .post(APIurl, { file: dataUrl, filename: fileName, }) diff --git a/optuna_dashboard/ts/components/Note.tsx b/optuna_dashboard/ts/components/Note.tsx index 09270213..586f34e0 100644 --- a/optuna_dashboard/ts/components/Note.tsx +++ b/optuna_dashboard/ts/components/Note.tsx @@ -425,7 +425,7 @@ const ArtifactUploader: FC<{ if (files === null) { return } - action.uploadArtifact(studyId, trialId, files[0]) + action.uploadTrialArtifact(studyId, trialId, files[0]) } const handleDrop: DragEventHandler = (e) => { @@ -433,7 +433,7 @@ const ArtifactUploader: FC<{ e.preventDefault() const file = e.dataTransfer.files[0] setDragOver(false) - action.uploadArtifact(studyId, trialId, file) + action.uploadTrialArtifact(studyId, trialId, file) } const handleDragOver: DragEventHandler = (e) => { diff --git a/optuna_dashboard/ts/components/StudyArtifactCards.tsx b/optuna_dashboard/ts/components/StudyArtifactCards.tsx new file mode 100644 index 00000000..32f36830 --- /dev/null +++ b/optuna_dashboard/ts/components/StudyArtifactCards.tsx @@ -0,0 +1,232 @@ +import React, { + FC, + useState, + DragEventHandler, + useRef, + MouseEventHandler, + ChangeEventHandler, +} from "react" +import { + Typography, + Box, + Card, + useTheme, + CardContent, + CardActionArea, + IconButton, +} from "@mui/material" +import { ArtifactCardMedia } from "./ArtifactCardMedia" +import FullscreenIcon from "@mui/icons-material/Fullscreen" +import UploadFileIcon from "@mui/icons-material/UploadFile" +import DownloadIcon from "@mui/icons-material/Download" +import { actionCreator } from "../action" + +import { + isThreejsArtifact, + useThreejsArtifactModal, +} from "./ThreejsArtifactViewer" + +export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { + const theme = useTheme() + const height = "150px" + const width = "200px" + + const [openThreejsArtifactModal, renderThreejsArtifactModal] = + useThreejsArtifactModal() + + return ( + <> + + Study Artifacts Test + + + + {study.artifacts.map((artifact) => { + const urlPath = `/artifacts/${study.id}/${artifact.artifact_id}` + return ( + + + + + {artifact.filename} + + {isThreejsArtifact(artifact) ? ( + { + openThreejsArtifactModal(urlPath, artifact) + }} + > + + + ) : null} + {/* TODO(gen740): add delete functionality + { + openDeleteArtifactDialog( + trial.study_id, + trial.trial_id, + artifact + ) + }} + > + + */} + + + + + + ) + })} + + + {renderThreejsArtifactModal()} + + ) +} + +const StudyArtifactUploader: FC<{ + study: StudyDetail + width: string + height: string +}> = ({ study, width, height }) => { + const theme = useTheme() + const [dragOver, setDragOver] = useState(false) + const action = actionCreator() + + const inputRef = useRef(null) + const handleClick: MouseEventHandler = () => { + if (!inputRef || !inputRef.current) { + return + } + inputRef.current.click() + } + + const handleOnChange: ChangeEventHandler = (e) => { + const files = e.target.files + if (files === null) { + return + } + action.uploadStudyArtifact(study.id, files[0]) + } + + const handleDragOver: DragEventHandler = (e) => { + e.stopPropagation() + e.preventDefault() + e.dataTransfer.dropEffect = "copy" + setDragOver(true) + } + + const handleDragLeave: DragEventHandler = (e) => { + e.stopPropagation() + e.preventDefault() + e.dataTransfer.dropEffect = "copy" + setDragOver(false) + } + + const handleDrop: DragEventHandler = (e) => { + e.stopPropagation() + e.preventDefault() + const files = e.dataTransfer.files + setDragOver(false) + for (let i = 0; i < files.length; i++) { + action.uploadStudyArtifact(study.id, files[i]) + } + } + + return ( + + + + + + Upload a New File + + Drag your file here or click to browse. + + + + + ) +} diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx index 907acd1a..e89a0ee4 100644 --- a/optuna_dashboard/ts/components/StudyHistory.tsx +++ b/optuna_dashboard/ts/components/StudyHistory.tsx @@ -17,12 +17,15 @@ import { DataGrid, DataGridColumn } from "./DataGrid" import { GraphHyperparameterImportance } from "./GraphHyperparameterImportances" import { UserDefinedPlot } from "./UserDefinedPlot" import { BestTrialsCard } from "./BestTrialsCard" +import { StudyArtifactCards } from "./StudyArtifactCards" +import { useRecoilValue } from "recoil" import { useStudyDetailValue, useStudyDirections, useStudySummaryValue, } from "../state" import FormControlLabel from "@mui/material/FormControlLabel" +import { artifactIsAvailable } from "../state" export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { const theme = useTheme() @@ -31,6 +34,7 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { const studyDetail = useStudyDetailValue(studyId) const [logScale, setLogScale] = useState(false) const [includePruned, setIncludePruned] = useState(true) + const artifactEnabled = useRecoilValue(artifactIsAvailable) const handleLogScaleChange = () => { setLogScale(!logScale) @@ -167,6 +171,16 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { + + + + + {artifactEnabled && studyDetail !== null && ( + + )} + + + ) } diff --git a/optuna_dashboard/ts/components/TrialArtifactCards.tsx b/optuna_dashboard/ts/components/TrialArtifactCards.tsx index 3e15f7ed..edbbce65 100644 --- a/optuna_dashboard/ts/components/TrialArtifactCards.tsx +++ b/optuna_dashboard/ts/components/TrialArtifactCards.tsx @@ -158,7 +158,7 @@ const TrialArtifactUploader: FC<{ if (files === null) { return } - action.uploadArtifact(trial.study_id, trial.trial_id, files[0]) + action.uploadTrialArtifact(trial.study_id, trial.trial_id, files[0]) } const handleDrop: DragEventHandler = (e) => { e.stopPropagation() @@ -166,7 +166,7 @@ const TrialArtifactUploader: FC<{ const files = e.dataTransfer.files setDragOver(false) for (let i = 0; i < files.length; i++) { - action.uploadArtifact(trial.study_id, trial.trial_id, files[i]) + action.uploadTrialArtifact(trial.study_id, trial.trial_id, files[i]) } } const handleDragOver: DragEventHandler = (e) => { From ee36eefcdcd8718ef0deb17c2b85f6b9b0860d7f Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 20 Oct 2023 17:29:54 +0900 Subject: [PATCH 05/99] reorder import statements --- .../ts/components/StudyArtifactCards.tsx | 35 ++++++++++--------- 1 file changed, 19 insertions(+), 16 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyArtifactCards.tsx b/optuna_dashboard/ts/components/StudyArtifactCards.tsx index 32f36830..9d9f35f9 100644 --- a/optuna_dashboard/ts/components/StudyArtifactCards.tsx +++ b/optuna_dashboard/ts/components/StudyArtifactCards.tsx @@ -1,39 +1,43 @@ import React, { - FC, - useState, - DragEventHandler, - useRef, - MouseEventHandler, ChangeEventHandler, + DragEventHandler, + FC, + MouseEventHandler, + useRef, + useState, } from "react" import { Typography, Box, - Card, useTheme, + IconButton, + Card, CardContent, CardActionArea, - IconButton, } from "@mui/material" -import { ArtifactCardMedia } from "./ArtifactCardMedia" -import FullscreenIcon from "@mui/icons-material/Fullscreen" import UploadFileIcon from "@mui/icons-material/UploadFile" import DownloadIcon from "@mui/icons-material/Download" -import { actionCreator } from "../action" +import DeleteIcon from "@mui/icons-material/Delete" +import FullscreenIcon from "@mui/icons-material/Fullscreen" +import { actionCreator } from "../action" +import { useDeleteArtifactDialog } from "./DeleteArtifactDialog" import { - isThreejsArtifact, useThreejsArtifactModal, + isThreejsArtifact, } from "./ThreejsArtifactViewer" +import { ArtifactCardMedia } from "./ArtifactCardMedia" export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { const theme = useTheme() - const height = "150px" - const width = "200px" - + const [openDeleteArtifactDialog, renderDeleteArtifactDialog] = + useDeleteArtifactDialog() const [openThreejsArtifactModal, renderThreejsArtifactModal] = useThreejsArtifactModal() + const width = "200px" + const height = "150px" + return ( <> = ({ study }) => { ) : null} - {/* TODO(gen740): add delete functionality = ({ study }) => { }} > - */} + Date: Fri, 20 Oct 2023 18:04:29 +0900 Subject: [PATCH 06/99] Add delete study artifact api --- optuna_dashboard/artifact/_backend.py | 29 ++++- optuna_dashboard/ts/action.ts | 55 ++++++-- optuna_dashboard/ts/apiClient.ts | 37 +++++- .../ts/components/DeleteArtifactDialog.tsx | 122 +++++++++++++----- .../ts/components/StudyArtifactCards.tsx | 11 +- .../ts/components/TrialArtifactCards.tsx | 4 +- python_tests/artifact/test_backend.py | 2 +- 7 files changed, 195 insertions(+), 65 deletions(-) diff --git a/optuna_dashboard/artifact/_backend.py b/optuna_dashboard/artifact/_backend.py index ee1b85a8..55dc844c 100644 --- a/optuna_dashboard/artifact/_backend.py +++ b/optuna_dashboard/artifact/_backend.py @@ -179,7 +179,7 @@ def register_artifact_route( @app.delete("/api/artifacts///") @json_api_view - def delete_artifact(study_id: int, trial_id: int, artifact_id: str) -> dict[str, Any]: + def delete_trial_artifact(study_id: int, trial_id: int, artifact_id: str) -> dict[str, Any]: if artifact_store is None: response.status = 400 # Bad Request return {"reason": "Cannot access to the artifacts."} @@ -187,7 +187,7 @@ def register_artifact_route( # The artifact's metadata is stored in one of the following two locations: storage.set_study_system_attr( - study_id, _dashboard_trial_artifact_prefix(trial_id) + artifact_id, json.dumps(None) + study_id, _dashboard_artifact_prefix(trial_id) + artifact_id, json.dumps(None) ) storage.set_trial_system_attr( trial_id, ARTIFACTS_ATTR_PREFIX + artifact_id, json.dumps(None) @@ -196,6 +196,25 @@ def register_artifact_route( response.status = 204 return {} + @app.delete("/api/artifacts//") + @json_api_view + def delete_study_artifact(study_id: int, artifact_id: str) -> dict[str, Any]: + if artifact_store is None: + response.status = 400 # Bad Request + return {"reason": "Cannot access to the artifacts."} + artifact_store.remove(artifact_id) + + # The artifact's metadata is stored in one of the following two locations: + storage.set_study_system_attr( + study_id, _dashboard_artifact_prefix(study_id) + artifact_id, json.dumps(None) + ) + storage.set_study_system_attr( + study_id, ARTIFACTS_ATTR_PREFIX + artifact_id, json.dumps(None) + ) + + response.status = 204 + return {} + def upload_artifact( backend: ArtifactBackend, @@ -253,7 +272,7 @@ def upload_artifact( return artifact_id -def _dashboard_trial_artifact_prefix(trial_id: int) -> str: +def _dashboard_artifact_prefix(trial_id: int) -> str: return DASHBOARD_ARTIFACTS_ATTR_PREFIX + f"{trial_id}:" @@ -273,7 +292,7 @@ def get_trial_artifact_meta( ) -> Optional[ArtifactMeta]: # Search study_system_attrs due to backward compatibility. study_system_attrs = storage.get_study_system_attrs(study_id) - attr_key = _dashboard_trial_artifact_prefix(trial_id=trial_id) + artifact_id + attr_key = _dashboard_artifact_prefix(trial_id=trial_id) + artifact_id artifact_meta = study_system_attrs.get(attr_key) if artifact_meta is not None: return json.loads(artifact_meta) @@ -317,7 +336,7 @@ def list_trial_artifacts( dashboard_artifact_metas = [ json.loads(value) for key, value in study_system_attrs.items() - if key.startswith(_dashboard_trial_artifact_prefix(trial._trial_id)) + if key.startswith(_dashboard_artifact_prefix(trial._trial_id)) ] # Collect ArtifactMeta from trial_system_attrs. Note that artifacts uploaded via diff --git a/optuna_dashboard/ts/action.ts b/optuna_dashboard/ts/action.ts index cfcf4c24..e3be3d85 100644 --- a/optuna_dashboard/ts/action.ts +++ b/optuna_dashboard/ts/action.ts @@ -11,9 +11,11 @@ import { tellTrialAPI, saveTrialUserAttrsAPI, renameStudyAPI, - uploadArtifactAPI, + uploadTrialArtifactAPI, + uploadStudyArtifactAPI, getMetaInfoAPI, - deleteArtifactAPI, + deleteTrialArtifactAPI, + deleteStudyArtifactAPI, reportPreferenceAPI, skipPreferentialTrialAPI, removePreferentialHistoryAPI, @@ -106,7 +108,7 @@ export const actionCreator = () => { setStudyDetailState(studyId, newStudy) } - const deleteTrialArtifact = ( + const deleteTrialArtifactState = ( studyId: number, trialId: number, artifact_id: string @@ -128,6 +130,18 @@ export const actionCreator = () => { setTrialArtifacts(studyId, index, newArtifacts) } + const deleteStudyArtifactState = (studyId: number, artifact_id: string) => { + const artifacts = studyDetails[studyId].artifacts + const artifactIndex = artifacts.findIndex( + (a) => a.artifact_id === artifact_id + ) + const newArtifacts = [ + ...artifacts.slice(0, artifactIndex), + ...artifacts.slice(artifactIndex + 1, artifacts.length), + ] + setStudyArtifacts(studyId, newArtifacts) + } + const setTrialStateValues = ( studyId: number, index: number, @@ -445,7 +459,7 @@ export const actionCreator = () => { setUploading(true) reader.readAsDataURL(file) reader.onload = (upload: ProgressEvent) => { - uploadArtifactAPI( + uploadTrialArtifactAPI( studyId, trialId, file.name, @@ -473,17 +487,13 @@ export const actionCreator = () => { } } - const uploadStudyArtifact = ( - studyId: number, - file: File - ): void => { + const uploadStudyArtifact = (studyId: number, file: File): void => { const reader = new FileReader() setUploading(true) reader.readAsDataURL(file) reader.onload = (upload: ProgressEvent) => { - uploadArtifactAPI( + uploadStudyArtifactAPI( studyId, - null, file.name, upload.target?.result as string ) @@ -503,14 +513,30 @@ export const actionCreator = () => { } } - const deleteArtifact = ( + const deleteTrialArtifact = ( studyId: number, trialId: number, artifactId: string ): void => { - deleteArtifactAPI(studyId, trialId, artifactId) + deleteTrialArtifactAPI(studyId, trialId, artifactId) .then(() => { - deleteTrialArtifact(studyId, trialId, artifactId) + deleteTrialArtifactState(studyId, trialId, artifactId) + enqueueSnackbar(`Success to delete an artifact.`, { + variant: "success", + }) + }) + .catch((err) => { + const reason = err.response?.data.reason + enqueueSnackbar(`Failed to delete ${reason}.`, { + variant: "error", + }) + }) + } + + const deleteStudyArtifact = (studyId: number, artifactId: string): void => { + deleteStudyArtifactAPI(studyId, artifactId) + .then(() => { + deleteStudyArtifactState(studyId, artifactId) enqueueSnackbar(`Success to delete an artifact.`, { variant: "success", }) @@ -731,7 +757,8 @@ export const actionCreator = () => { saveTrialNote, uploadTrialArtifact, uploadStudyArtifact, - deleteArtifact, + deleteTrialArtifact, + deleteStudyArtifact, makeTrialComplete, makeTrialFail, saveTrialUserAttrs, diff --git a/optuna_dashboard/ts/apiClient.ts b/optuna_dashboard/ts/apiClient.ts index c1dedd14..f42d20de 100644 --- a/optuna_dashboard/ts/apiClient.ts +++ b/optuna_dashboard/ts/apiClient.ts @@ -280,17 +280,14 @@ type UploadArtifactAPIResponse = { artifacts: Artifact[] } -export const uploadArtifactAPI = ( +export const uploadTrialArtifactAPI = ( studyId: number, - trialId: number | null, + trialId: number, fileName: string, dataUrl: string ): Promise => { - const APIurl = `/api/artifacts/${studyId}${ - trialId != null ? `/${trialId}` : "" - }` return axiosInstance - .post(APIurl, { + .post(`/api/artifacts/${studyId}/${trialId}`, { file: dataUrl, filename: fileName, }) @@ -299,7 +296,22 @@ export const uploadArtifactAPI = ( }) } -export const deleteArtifactAPI = ( +export const uploadStudyArtifactAPI = ( + studyId: number, + fileName: string, + dataUrl: string +): Promise => { + return axiosInstance + .post(`/api/artifacts/${studyId}`, { + file: dataUrl, + filename: fileName, + }) + .then((res) => { + return res.data + }) +} + +export const deleteTrialArtifactAPI = ( studyId: number, trialId: number, artifactId: string @@ -311,6 +323,17 @@ export const deleteArtifactAPI = ( }) } +export const deleteStudyArtifactAPI = ( + studyId: number, + artifactId: string +): Promise => { + return axiosInstance + .delete(`/api/artifacts/${studyId}/${artifactId}`) + .then(() => { + return + }) +} + export const tellTrialAPI = ( trialId: number, state: TrialStateFinished, diff --git a/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx b/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx index 2c9c229e..4bf5e95d 100644 --- a/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx +++ b/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx @@ -1,4 +1,4 @@ -import React, { ReactNode, useState } from "react" +import React, { ReactNode, useState, FC } from "react" import { Dialog, DialogTitle, @@ -9,9 +9,9 @@ import { } from "@mui/material" import { actionCreator } from "../action" -export const useDeleteArtifactDialog = (): [ +export const useDeleteTrialArtifactDialog = (): [ (studyId: number, trialId: number, artifact: Artifact) => void, - () => ReactNode + () => ReactNode, ] => { const action = actionCreator() @@ -33,7 +33,7 @@ export const useDeleteArtifactDialog = (): [ if (artifact === null) { return } - action.deleteArtifact(studyId, trialId, artifact.artifact_id) + action.deleteTrialArtifact(studyId, trialId, artifact.artifact_id) setOpenDeleteArtifactDialog(false) setTarget([-1, -1, null]) } @@ -45,32 +45,96 @@ export const useDeleteArtifactDialog = (): [ const renderDeleteArtifactDialog = () => { return ( - { - handleCloseDeleteArtifactDialog() - }} - aria-labelledby="delete-artifact-dialog-title" - > - - Delete artifact - - - - Are you sure you want to delete an artifact (" - {target[2]?.filename}")? - - - - - - - + ) } return [openDialog, renderDeleteArtifactDialog] } + +export const useDeleteStudyArtifactDialog = (): [ + (studyId: number, artifact: Artifact) => void, + () => ReactNode, +] => { + const action = actionCreator() + + const [openDeleteArtifactDialog, setOpenDeleteArtifactDialog] = + useState(false) + const [target, setTarget] = useState<[number, Artifact | null]>([-1, null]) + + const handleCloseDeleteArtifactDialog = () => { + setOpenDeleteArtifactDialog(false) + setTarget([-1, null]) + } + + const handleDeleteArtifact = () => { + const [studyId, artifact] = target + if (artifact === null) { + return + } + action.deleteStudyArtifact(studyId, artifact.artifact_id) + setOpenDeleteArtifactDialog(false) + setTarget([-1, null]) + } + + const openDialog = (studyId: number, artifact: Artifact) => { + setTarget([studyId, artifact]) + setOpenDeleteArtifactDialog(true) + } + + const renderDeleteArtifactDialog = () => { + return ( + + ) + } + return [openDialog, renderDeleteArtifactDialog] +} + +const DeleteDialog: FC<{ + openDeleteArtifactDialog: boolean + handleCloseDeleteArtifactDialog: () => void + filename: string | undefined + handleDeleteArtifact: () => void +}> = ({ + openDeleteArtifactDialog, + handleCloseDeleteArtifactDialog, + filename, + handleDeleteArtifact, +}) => { + return ( + { + handleCloseDeleteArtifactDialog() + }} + aria-labelledby="delete-artifact-dialog-title" + > + + Delete artifact + + + + Are you sure you want to delete an artifact (" + {filename}")? + + + + + + + + ) +} diff --git a/optuna_dashboard/ts/components/StudyArtifactCards.tsx b/optuna_dashboard/ts/components/StudyArtifactCards.tsx index 9d9f35f9..71143ed4 100644 --- a/optuna_dashboard/ts/components/StudyArtifactCards.tsx +++ b/optuna_dashboard/ts/components/StudyArtifactCards.tsx @@ -21,7 +21,7 @@ import DeleteIcon from "@mui/icons-material/Delete" import FullscreenIcon from "@mui/icons-material/Fullscreen" import { actionCreator } from "../action" -import { useDeleteArtifactDialog } from "./DeleteArtifactDialog" +import { useDeleteStudyArtifactDialog } from "./DeleteArtifactDialog" import { useThreejsArtifactModal, isThreejsArtifact, @@ -31,7 +31,7 @@ import { ArtifactCardMedia } from "./ArtifactCardMedia" export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { const theme = useTheme() const [openDeleteArtifactDialog, renderDeleteArtifactDialog] = - useDeleteArtifactDialog() + useDeleteStudyArtifactDialog() const [openThreejsArtifactModal, renderThreejsArtifactModal] = useThreejsArtifactModal() @@ -104,11 +104,7 @@ export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { color="inherit" sx={{ margin: "auto 0" }} onClick={() => { - openDeleteArtifactDialog( - trial.study_id, - trial.trial_id, - artifact - ) + openDeleteArtifactDialog(study.id, artifact) }} > @@ -129,6 +125,7 @@ export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { })} + {renderDeleteArtifactDialog()} {renderThreejsArtifactModal()} ) diff --git a/optuna_dashboard/ts/components/TrialArtifactCards.tsx b/optuna_dashboard/ts/components/TrialArtifactCards.tsx index edbbce65..1a078f29 100644 --- a/optuna_dashboard/ts/components/TrialArtifactCards.tsx +++ b/optuna_dashboard/ts/components/TrialArtifactCards.tsx @@ -21,7 +21,7 @@ import DeleteIcon from "@mui/icons-material/Delete" import FullscreenIcon from "@mui/icons-material/Fullscreen" import { actionCreator } from "../action" -import { useDeleteArtifactDialog } from "./DeleteArtifactDialog" +import { useDeleteTrialArtifactDialog } from "./DeleteArtifactDialog" import { useThreejsArtifactModal, isThreejsArtifact, @@ -31,7 +31,7 @@ import { ArtifactCardMedia } from "./ArtifactCardMedia" export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => { const theme = useTheme() const [openDeleteArtifactDialog, renderDeleteArtifactDialog] = - useDeleteArtifactDialog() + useDeleteTrialArtifactDialog() const [openThreejsArtifactModal, renderThreejsArtifactModal] = useThreejsArtifactModal() diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 74698a09..22544e6f 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -12,7 +12,7 @@ def test_get_artifact_path() -> None: def test_artifact_prefix() -> None: - actual = _backend._dashboard_trial_artifact_prefix(trial_id=0) + actual = _backend._dashboard_artifact_prefix(trial_id=0) assert actual == "dashboard:artifacts:0:" From f4405f033311c987e75febb24bbeb861128854a7 Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 27 Oct 2023 17:30:33 +0900 Subject: [PATCH 07/99] Add bordor to the Study Artifact Card Content --- .../ts/components/StudyArtifactCards.tsx | 8 +------ .../ts/components/StudyHistory.tsx | 22 ++++++++++++++++--- 2 files changed, 20 insertions(+), 10 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyArtifactCards.tsx b/optuna_dashboard/ts/components/StudyArtifactCards.tsx index 71143ed4..c6394896 100644 --- a/optuna_dashboard/ts/components/StudyArtifactCards.tsx +++ b/optuna_dashboard/ts/components/StudyArtifactCards.tsx @@ -40,13 +40,6 @@ export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { return ( <> - - Study Artifacts Test - - {study.artifacts.map((artifact) => { const urlPath = `/artifacts/${study.id}/${artifact.artifact_id}` @@ -57,6 +50,7 @@ export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => { marginBottom: theme.spacing(2), width: width, margin: theme.spacing(0, 1, 1, 0), + border: `1px solid ${theme.palette.divider}`, }} > = ({ studyId }) => { - {artifactEnabled && studyDetail !== null && ( - - )} + + + Study Artifacts + + {artifactEnabled && studyDetail !== null && ( + + )} + From d8bdea0a8c6b4c6de703b2764424509ee72807d4 Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 27 Oct 2023 18:10:34 +0900 Subject: [PATCH 08/99] Delete delete function when artifact is not modifiable --- .../ts/components/TrialArtifactCards.tsx | 52 ++++++++++--------- 1 file changed, 28 insertions(+), 24 deletions(-) diff --git a/optuna_dashboard/ts/components/TrialArtifactCards.tsx b/optuna_dashboard/ts/components/TrialArtifactCards.tsx index 3e15f7ed..d4d00e4c 100644 --- a/optuna_dashboard/ts/components/TrialArtifactCards.tsx +++ b/optuna_dashboard/ts/components/TrialArtifactCards.tsx @@ -34,6 +34,9 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => { useDeleteArtifactDialog() const [openThreejsArtifactModal, renderThreejsArtifactModal] = useThreejsArtifactModal() + const isArtifactModifiable = (trial: Trial) => { + return trial.state === "Running" || trial.state === "Waiting" + } const width = "200px" const height = "150px" @@ -75,11 +78,11 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => { p: theme.spacing(0.5, 0), flexGrow: 1, wordWrap: "break-word", - maxWidth: `calc(100% - ${ - isThreejsArtifact(artifact) - ? theme.spacing(12) - : theme.spacing(8) - })`, + maxWidth: `calc(100% - ${theme.spacing( + 4 + + (isThreejsArtifact(artifact) ? 4 : 0) + + (isArtifactModifiable(trial) ? 4 : 0) + )})`, }} > {artifact.filename} @@ -97,21 +100,23 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => { ) : null} - { - openDeleteArtifactDialog( - trial.study_id, - trial.trial_id, - artifact - ) - }} - > - - + {isArtifactModifiable(trial) ? ( + { + openDeleteArtifactDialog( + trial.study_id, + trial.trial_id, + artifact + ) + }} + > + + + ) : null} = ({ trial }) => { ) })} - + {isArtifactModifiable(trial) ? ( + + ) : null} {renderDeleteArtifactDialog()} {renderThreejsArtifactModal()} @@ -143,9 +150,6 @@ const TrialArtifactUploader: FC<{ const action = actionCreator() const [dragOver, setDragOver] = useState(false) - if (trial.state !== "Running" && trial.state !== "Waiting") { - return null - } const inputRef = useRef(null) const handleClick: MouseEventHandler = () => { if (!inputRef || !inputRef.current) { From 7dbef5050115948aeb5508e84fcca10d9730a15a Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 3 Nov 2023 04:02:35 +0900 Subject: [PATCH 09/99] Apply Format --- optuna_dashboard/ts/components/DeleteArtifactDialog.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx b/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx index 4bf5e95d..7ac46162 100644 --- a/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx +++ b/optuna_dashboard/ts/components/DeleteArtifactDialog.tsx @@ -11,7 +11,7 @@ import { actionCreator } from "../action" export const useDeleteTrialArtifactDialog = (): [ (studyId: number, trialId: number, artifact: Artifact) => void, - () => ReactNode, + () => ReactNode ] => { const action = actionCreator() @@ -58,7 +58,7 @@ export const useDeleteTrialArtifactDialog = (): [ export const useDeleteStudyArtifactDialog = (): [ (studyId: number, artifact: Artifact) => void, - () => ReactNode, + () => ReactNode ] => { const action = actionCreator() From 2965e69508d4747d0b5bf9a43575fd498acbab1d Mon Sep 17 00:00:00 2001 From: Victoria A <52001888+adjeiv@users.noreply.github.com> Date: Sun, 12 Nov 2023 10:49:51 +0000 Subject: [PATCH 10/99] Retain trial notes --- optuna_dashboard/_app.py | 4 +++- optuna_dashboard/_note.py | 40 +++++++++++++++++++++++++++++++++----- python_tests/test_note.py | 41 +++++++++++++++++++++++++++++++++++++++ 3 files changed, 79 insertions(+), 6 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 812201ad..9ce820b7 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -164,7 +164,8 @@ def create_app( if new_study_summary is None: response.status = 500 return {"reason": "Failed to load the new study"} - + + note.transfer_notes(storage, src_study, dst_study) storage.delete_study(src_study._study_id) response.status = 201 return serialize_study_summary(new_study_summary) @@ -176,6 +177,7 @@ def create_app( delete_all_artifacts(artifact_store, storage, study_id) try: + note.delete_study_notes(storage, study_id) storage.delete_study(study_id) except KeyError: response.status = 404 # Not found diff --git a/optuna_dashboard/_note.py b/optuna_dashboard/_note.py index 95853a3d..e510ddfd 100644 --- a/optuna_dashboard/_note.py +++ b/optuna_dashboard/_note.py @@ -109,6 +109,21 @@ def note_str_key_prefix(trial_id: Optional[int]) -> str: return prefix return f"dashboard:{trial_id}:note_str:" +def transfer_notes(storage: BaseStorage, src_study: optuna.Study, dst_study: optuna.Study) -> None: + system_attrs = storage.get_study_system_attrs(study_id=src_study._study_id) + + def transfer(src_trial_id: Optional[int], dst_trial_id: Optional[int]) -> None: + note = get_note_from_system_attrs(system_attrs, src_trial_id)["body"] + save_note_with_version(storage, dst_study._study_id, dst_trial_id, 0, note) + delete_notes(storage, src_study._study_id, src_trial_id) + + # Transfer individual trial notes + for src_trial, dst_trial in zip(src_study.get_trials(), dst_study.get_trials()): + transfer(src_trial._trial_id, dst_trial._trial_id) + + # Transfer study note + NO_SRC_TRIAL, NO_DST_TRIAL = None, None + transfer(NO_SRC_TRIAL, NO_DST_TRIAL) def get_note_from_system_attrs(system_attrs: dict[str, Any], trial_id: Optional[int]) -> NoteType: if note_ver_key(trial_id) not in system_attrs: @@ -131,6 +146,13 @@ def version_is_incremented( db_note_ver = system_attrs.get(note_ver_key(trial_id), 0) return req_note_ver == db_note_ver + 1 +def all_trial_notes(storage: BaseStorage, study_id: int, trial_id: Optional[int]) -> dict[str, str]: + all_note_attrs: dict[str, str] = { + key: value + for key, value in storage.get_study_system_attrs(study_id).items() + if key.startswith(note_str_key_prefix(trial_id)) + } + return all_note_attrs def save_note_with_version( storage: BaseStorage, study_id: int, trial_id: Optional[int], ver: int, body: str @@ -142,15 +164,23 @@ def save_note_with_version( storage.set_study_system_attr(study_id, k, v) # Clear previous messages - all_note_attrs: dict[str, str] = { - key: value - for key, value in storage.get_study_system_attrs(study_id).items() - if key.startswith(note_str_key_prefix(trial_id)) - } + all_note_attrs = all_trial_notes(storage, study_id, trial_id) if len(all_note_attrs) > len(attrs): for i in range(len(attrs), len(all_note_attrs)): storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") +def delete_study_notes(storage: BaseStorage, study_id): + study = storage.get_study_name_from_id(study_id) + for trial in storage.get_all_trials(study_id): + delete_notes(storage, study_id, trial._trial_id) + + delete_notes(storage, study_id, None) + +def delete_notes(storage: BaseStorage, study_id: int, trial_id: Optional[int]) -> None: + all_note_attrs = all_trial_notes(storage, study_id, trial_id) + + for i in range(len(all_note_attrs)): + storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") def split_body(note_str: str, trial_id: Optional[int]) -> dict[str, str]: note_len = len(note_str) diff --git a/python_tests/test_note.py b/python_tests/test_note.py index 7cc02cd4..4997d16a 100644 --- a/python_tests/test_note.py +++ b/python_tests/test_note.py @@ -53,3 +53,44 @@ class NoteTestCase(TestCase): note_dict = note.get_note_from_system_attrs(system_attrs, trial._trial_id) self.assertEqual(note_dict["body"], body) self.assertEqual(note_dict["version"], expected_ver) + + def test_delete_notes_trial(self) -> None: + study = optuna.create_study() + trial_1 = study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) + trial_2 = study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) + storage = study._storage + + for trial, body in [(trial_1, "version 1"), (trial_2, "version 2")]: + save_note(trial, body) + + # first assert existence + actual = get_note(trial) + self.assertEqual(actual, body) + + # delete + note.delete_notes(storage, study._study_id, trial._trial_id) + + # assert deletion + actual = get_note(trial) + self.assertEqual(actual, "") + + + note.delete_study_notes(storage, study._study_id) + + def test_delete_notes_study(self) -> None: + pass + + def test_transfer_notes(self) -> None: + study = optuna.create_study() + trial_1 = study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) + trial_2 = study.ask({"x2": optuna.distributions.FloatDistribution(0, 10)}) + storage = study._storage + + save_note(trial_1, "trial 1") + save_note(trial_2, "trial 2") + + new_study = optuna.create_study( + storage=storage, directions=study.directions + ) + note.transfer_notes(storage, study, new_study) + From f04c2d7d7bae0cd667eec056bc0a35b19f30b846 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 5 Nov 2023 16:32:18 +0900 Subject: [PATCH 11/99] Move getAxis implementation to graphUtil --- .../ts/components/GraphContour.tsx | 109 +---------------- optuna_dashboard/ts/graphUtil.ts | 112 ++++++++++++++++++ 2 files changed, 113 insertions(+), 108 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphContour.tsx b/optuna_dashboard/ts/components/GraphContour.tsx index a5416f5b..b7895ddf 100644 --- a/optuna_dashboard/ts/components/GraphContour.tsx +++ b/optuna_dashboard/ts/components/GraphContour.tsx @@ -14,25 +14,8 @@ import { import blue from "@mui/material/colors/blue" import { plotlyDarkTemplate } from "./PlotlyDarkMode" import { useMergedUnionSearchSpace } from "../searchSpace" +import { getAxisInfo } from "../graphUtil" -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const unique = (array: any[]) => { - const knownElements = new Map() - array.forEach((elem) => knownElements.set(elem, true)) - return Array.from(knownElements.keys()) -} - -type AxisInfo = { - name: string - min: number - max: number - isLog: boolean - isCat: boolean - indices: (string | number)[] - values: (string | number | null)[] -} - -const PADDING_RATIO = 0.05 const plotDomId = "graph-contour" export const Contour: FC<{ @@ -284,93 +267,3 @@ const plotContour = ( ] plotly.react(plotDomId, plotData, layout) } - -const getAxisInfoForNumericalParams = ( - trials: Trial[], - paramName: string, - distribution: FloatDistribution | IntDistribution -): AxisInfo => { - let min = 0 - let max = 0 - if (distribution.log) { - const padding = - (Math.log10(distribution.high) - Math.log10(distribution.low)) * - PADDING_RATIO - min = Math.pow(10, Math.log10(distribution.low) - padding) - max = Math.pow(10, Math.log10(distribution.high) + padding) - } else { - const padding = (distribution.high - distribution.low) * PADDING_RATIO - min = distribution.low - padding - max = distribution.high + padding - } - - const values = trials.map( - (trial) => - trial.params.find((p) => p.name === paramName)?.param_internal_value || - null - ) - const indices = unique(values) - .filter((v) => v !== null) - .sort((a, b) => a - b) - if (indices.length >= 2) { - indices.unshift(min) - indices.push(max) - } - return { - name: paramName, - min, - max, - isLog: distribution.log, - isCat: false, - indices, - values, - } -} - -const getAxisInfoForCategoricalParams = ( - trials: Trial[], - paramName: string, - distribution: CategoricalDistribution -): AxisInfo => { - const values = trials.map( - (trial) => - trial.params.find((p) => p.name === paramName)?.param_external_value || - null - ) - const isDynamic = values.some((v) => v === null) - const span = distribution.choices.length - (isDynamic ? 2 : 1) - const padding = span * PADDING_RATIO - const min = -padding - const max = span + padding - - const indices = distribution.choices - .map((c) => c.value) - .sort((a, b) => - a.toLowerCase() < b.toLowerCase() - ? -1 - : a.toLowerCase() > b.toLowerCase() - ? 1 - : 0 - ) - return { - name: paramName, - min, - max, - isLog: false, - isCat: true, - indices, - values, - } -} - -const getAxisInfo = (trials: Trial[], param: SearchSpaceItem): AxisInfo => { - if (param.distribution.type === "CategoricalDistribution") { - return getAxisInfoForCategoricalParams( - trials, - param.name, - param.distribution - ) - } else { - return getAxisInfoForNumericalParams(trials, param.name, param.distribution) - } -} diff --git a/optuna_dashboard/ts/graphUtil.ts b/optuna_dashboard/ts/graphUtil.ts index 1b1c69cb..4e6f74e8 100644 --- a/optuna_dashboard/ts/graphUtil.ts +++ b/optuna_dashboard/ts/graphUtil.ts @@ -1,3 +1,115 @@ +const PADDING_RATIO = 0.05 + +type AxisInfo = { + name: string + min: number + max: number + isLog: boolean + isCat: boolean + indices: (string | number)[] + values: (string | number | null)[] +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const unique = (array: any[]) => { + const knownElements = new Map() + array.forEach((elem) => knownElements.set(elem, true)) + return Array.from(knownElements.keys()) +} + +export const getAxisInfo = ( + trials: Trial[], + param: SearchSpaceItem +): AxisInfo => { + if (param.distribution.type === "CategoricalDistribution") { + return getAxisInfoForCategoricalParams( + trials, + param.name, + param.distribution + ) + } else { + return getAxisInfoForNumericalParams(trials, param.name, param.distribution) + } +} + +const getAxisInfoForCategoricalParams = ( + trials: Trial[], + paramName: string, + distribution: CategoricalDistribution +): AxisInfo => { + const values = trials.map( + (trial) => + trial.params.find((p) => p.name === paramName)?.param_external_value || + null + ) + const isDynamic = values.some((v) => v === null) + const span = distribution.choices.length - (isDynamic ? 2 : 1) + const padding = span * PADDING_RATIO + const min = -padding + const max = span + padding + + const indices = distribution.choices + .map((c) => c.value) + .sort((a, b) => + a.toLowerCase() < b.toLowerCase() + ? -1 + : a.toLowerCase() > b.toLowerCase() + ? 1 + : 0 + ) + return { + name: paramName, + min, + max, + isLog: false, + isCat: true, + indices, + values, + } +} + +const getAxisInfoForNumericalParams = ( + trials: Trial[], + paramName: string, + distribution: FloatDistribution | IntDistribution +): AxisInfo => { + let min = 0 + let max = 0 + if (distribution.log) { + const padding = + (Math.log10(distribution.high) - Math.log10(distribution.low)) * + PADDING_RATIO + min = Math.pow(10, Math.log10(distribution.low) - padding) + max = Math.pow(10, Math.log10(distribution.high) + padding) + } else { + const padding = (distribution.high - distribution.low) * PADDING_RATIO + min = distribution.low - padding + max = distribution.high + padding + } + + const values = trials.map( + (trial) => + trial.params.find((p) => p.name === paramName)?.param_internal_value || + null + ) + const indices = unique(values) + .filter((v) => v !== null) + .sort((a, b) => a - b) + if (indices.length >= 2) { + indices.unshift(min) + indices.push(max) + } + return { + name: paramName, + min, + max, + isLog: distribution.log, + isCat: false, + indices, + values, + } +} + export const makeHovertext = (trial: Trial): string => { return JSON.stringify( { From 0df7ffa0f59f97127f8ef3673ea4847ee27f0397 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 5 Nov 2023 16:54:48 +0900 Subject: [PATCH 12/99] Use implementation in graphUtil for rank plot --- optuna_dashboard/ts/components/GraphRank.tsx | 103 +++---------------- optuna_dashboard/ts/graphUtil.ts | 2 +- 2 files changed, 13 insertions(+), 92 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index 6781793f..f39def0e 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -12,19 +12,11 @@ import { Box, } from "@mui/material" import { plotlyDarkTemplate } from "./PlotlyDarkMode" -import { makeHovertext } from "../graphUtil" +import { AxisInfo, getAxisInfo, makeHovertext } from "../graphUtil" import { useMergedUnionSearchSpace } from "../searchSpace" -const PADDING_RATIO = 0.05 const plotDomId = "graph-rank" -interface AxisInfo { - name: string - range: [number, number] - isLog: boolean - isCat: boolean -} - interface RankPlotInfo { xaxis: AxisInfo yaxis: AxisInfo @@ -159,23 +151,18 @@ const getRankPlotInfo = ( const zValues: number[] = [] const isFeasible: boolean[] = [] const hovertext: string[] = [] - filteredTrials.forEach((trial) => { - const xValue = - trial.params.find((p) => p.name === xAxis.name)?.param_external_value || - null - const yValue = - trial.params.find((p) => p.name === yAxis.name)?.param_external_value || - null - if (trial.values === undefined || xValue === null || yValue === null) { - return + filteredTrials.forEach((trial, i) => { + if (xAxis.values[i] && yAxis.values[i] && trial.values) { + const xValue = xAxis.values[i] as string | number + const yValue = yAxis.values[i] as string | number + xValues.push(xValue) + yValues.push(yValue) + const zValue = Number(trial.values[objectiveId]) + zValues.push(zValue) + const feasibility = trial.constraints.every((c) => c <= 0) + isFeasible.push(feasibility) + hovertext.push(makeHovertext(trial)) } - const zValue = Number(trial.values[objectiveId]) - const feasibility = trial.constraints.every((c) => c <= 0) - xValues.push(xValue) - yValues.push(yValue) - zValues.push(zValue) - isFeasible.push(feasibility) - hovertext.push(makeHovertext(trial)) }) const colors = getColors(zValues) @@ -196,72 +183,6 @@ const filterFunc = (trial: Trial): boolean => { return trial.state === "Complete" && trial.values !== undefined } -const getAxisInfo = (trials: Trial[], param: SearchSpaceItem): AxisInfo => { - if (param.distribution.type === "CategoricalDistribution") { - return getAxisInfoForCategorical(trials, param.name, param.distribution) - } else { - return getAxisInfoForNumerical(trials, param.name, param.distribution) - } -} - -const getAxisInfoForCategorical = ( - trials: Trial[], - param: string, - distribution: CategoricalDistribution -): AxisInfo => { - const values = trials.map( - (trial) => - trial.params.find((p) => p.name === param)?.param_internal_value || null - ) - const isDynamic = values.some((v) => v === null) - const span = distribution.choices.length - (isDynamic ? 2 : 1) - const padding = span * PADDING_RATIO - const min = -padding - const max = span + padding - - return { - name: param, - range: [min, max], - isLog: false, - isCat: true, - } -} - -const getAxisInfoForNumerical = ( - trials: Trial[], - param: string, - distribution: FloatDistribution | IntDistribution -): AxisInfo => { - const values = trials.map( - (trial) => - trial.params.find((p) => p.name === param)?.param_internal_value || null - ) - const nonNullValues: number[] = [] - values.forEach((value) => { - if (value !== null) { - nonNullValues.push(value) - } - }) - let min = Math.min(...nonNullValues) - let max = Math.max(...nonNullValues) - if (distribution.log) { - const padding = (Math.log10(max) - Math.log10(min)) * PADDING_RATIO - min = Math.pow(10, Math.log10(min) - padding) - max = Math.pow(10, Math.log10(max) + padding) - } else { - const padding = (max - min) * PADDING_RATIO - min = min - padding - max = max + padding - } - - return { - name: param, - range: [min, max], - isLog: distribution.log, - isCat: false, - } -} - const getColors = (values: number[]): number[] => { const rawRanks = getOrderWithSameOrderAveraging(values) let colorIdxs: number[] = [] diff --git a/optuna_dashboard/ts/graphUtil.ts b/optuna_dashboard/ts/graphUtil.ts index 4e6f74e8..26f63971 100644 --- a/optuna_dashboard/ts/graphUtil.ts +++ b/optuna_dashboard/ts/graphUtil.ts @@ -1,6 +1,6 @@ const PADDING_RATIO = 0.05 -type AxisInfo = { +export type AxisInfo = { name: string min: number max: number From 55b2adf04fe2be8b3bff4d993746b629d9382cdb Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 5 Nov 2023 17:18:39 +0900 Subject: [PATCH 13/99] Delete min and max in AxisInfo --- optuna_dashboard/ts/graphUtil.ts | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/optuna_dashboard/ts/graphUtil.ts b/optuna_dashboard/ts/graphUtil.ts index 26f63971..35a9002b 100644 --- a/optuna_dashboard/ts/graphUtil.ts +++ b/optuna_dashboard/ts/graphUtil.ts @@ -2,8 +2,6 @@ const PADDING_RATIO = 0.05 export type AxisInfo = { name: string - min: number - max: number isLog: boolean isCat: boolean indices: (string | number)[] @@ -42,11 +40,6 @@ const getAxisInfoForCategoricalParams = ( trial.params.find((p) => p.name === paramName)?.param_external_value || null ) - const isDynamic = values.some((v) => v === null) - const span = distribution.choices.length - (isDynamic ? 2 : 1) - const padding = span * PADDING_RATIO - const min = -padding - const max = span + padding const indices = distribution.choices .map((c) => c.value) @@ -59,8 +52,6 @@ const getAxisInfoForCategoricalParams = ( ) return { name: paramName, - min, - max, isLog: false, isCat: true, indices, @@ -101,8 +92,6 @@ const getAxisInfoForNumericalParams = ( } return { name: paramName, - min, - max, isLog: distribution.log, isCat: false, indices, From 7f78b6dc0c32864fa4484e2c283bce902951eabb Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 5 Nov 2023 17:34:12 +0900 Subject: [PATCH 14/99] Change the definition of RankPlotInfo --- optuna_dashboard/ts/components/GraphRank.tsx | 115 ++++++++++--------- 1 file changed, 59 insertions(+), 56 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index f39def0e..dc0a3842 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -12,14 +12,16 @@ import { Box, } from "@mui/material" import { plotlyDarkTemplate } from "./PlotlyDarkMode" -import { AxisInfo, getAxisInfo, makeHovertext } from "../graphUtil" +import { getAxisInfo, makeHovertext } from "../graphUtil" import { useMergedUnionSearchSpace } from "../searchSpace" const plotDomId = "graph-rank" interface RankPlotInfo { - xaxis: AxisInfo - yaxis: AxisInfo + xtitle: string + ytitle: string + xtype: plotly.AxisType + ytype: plotly.AxisType xvalues: (string | number)[] yvalues: (string | number)[] zvalues: number[] @@ -146,8 +148,8 @@ const getRankPlotInfo = ( const xAxis = getAxisInfo(filteredTrials, xParam) const yAxis = getAxisInfo(filteredTrials, yParam) - const xValues: (string | number)[] = [] - const yValues: (string | number)[] = [] + let xValues: (string | number)[] = [] + let yValues: (string | number)[] = [] const zValues: number[] = [] const isFeasible: boolean[] = [] const hovertext: string[] = [] @@ -167,9 +169,53 @@ const getRankPlotInfo = ( const colors = getColors(zValues) + if (xAxis.isCat && !yAxis.isCat) { + const xIndices: number[] = Array.from(Array(xValues.length).keys()).sort( + (a, b) => + xValues[a] + .toString() + .toLowerCase() + .localeCompare(xValues[b].toString().toLowerCase()) + ) + xValues = xIndices.map((i) => xValues[i]) + yValues = xIndices.map((i) => yValues[i]) + } + if (!xAxis.isCat && yAxis.isCat) { + const yIndices: number[] = Array.from(Array(yValues.length).keys()).sort( + (a, b) => + yValues[a] + .toString() + .toLowerCase() + .localeCompare(yValues[b].toString().toLowerCase()) + ) + xValues = yIndices.map((i) => xValues[i]) + yValues = yIndices.map((i) => yValues[i]) + } + if (xAxis.isCat && yAxis.isCat) { + const indices: number[] = Array.from(Array(xValues.length).keys()).sort( + (a, b) => { + const xComp = xValues[a] + .toString() + .toLowerCase() + .localeCompare(xValues[b].toString().toLowerCase()) + if (xComp !== 0) { + return xComp + } + return yValues[a] + .toString() + .toLowerCase() + .localeCompare(yValues[b].toString().toLowerCase()) + } + ) + xValues = indices.map((i) => xValues[i]) + yValues = indices.map((i) => yValues[i]) + } + return { - xaxis: xAxis, - yaxis: yAxis, + xtitle: xAxis.name, + ytitle: yAxis.name, + xtype: xAxis.isCat ? "category" : xAxis.isLog ? "log" : "linear", + ytype: yAxis.isCat ? "category" : yAxis.isLog ? "log" : "linear", xvalues: xValues, yvalues: yValues, zvalues: zValues, @@ -220,16 +266,14 @@ const plotRank = (rankPlotInfo: RankPlotInfo | null, mode: string) => { return } - const xAxis = rankPlotInfo.xaxis - const yAxis = rankPlotInfo.yaxis const layout: Partial = { xaxis: { - title: xAxis.name, - type: xAxis.isCat ? "category" : xAxis.isLog ? "log" : "linear", + title: rankPlotInfo.xtitle, + type: rankPlotInfo.xtype, }, yaxis: { - title: yAxis.name, - type: yAxis.isCat ? "category" : yAxis.isLog ? "log" : "linear", + title: rankPlotInfo.ytitle, + type: rankPlotInfo.ytype, }, margin: { l: 50, @@ -241,49 +285,8 @@ const plotRank = (rankPlotInfo: RankPlotInfo | null, mode: string) => { template: mode === "dark" ? plotlyDarkTemplate : {}, } - let xValues = rankPlotInfo.xvalues - let yValues = rankPlotInfo.yvalues - if (xAxis.isCat && !yAxis.isCat) { - const xIndices: number[] = Array.from(Array(xValues.length).keys()).sort( - (a, b) => - xValues[a] - .toString() - .toLowerCase() - .localeCompare(xValues[b].toString().toLowerCase()) - ) - xValues = xIndices.map((i) => xValues[i]) - yValues = xIndices.map((i) => yValues[i]) - } - if (!xAxis.isCat && yAxis.isCat) { - const yIndices: number[] = Array.from(Array(yValues.length).keys()).sort( - (a, b) => - yValues[a] - .toString() - .toLowerCase() - .localeCompare(yValues[b].toString().toLowerCase()) - ) - xValues = yIndices.map((i) => xValues[i]) - yValues = yIndices.map((i) => yValues[i]) - } - if (xAxis.isCat && yAxis.isCat) { - const indices: number[] = Array.from(Array(xValues.length).keys()).sort( - (a, b) => { - const xComp = xValues[a] - .toString() - .toLowerCase() - .localeCompare(xValues[b].toString().toLowerCase()) - if (xComp !== 0) { - return xComp - } - return yValues[a] - .toString() - .toLowerCase() - .localeCompare(yValues[b].toString().toLowerCase()) - } - ) - xValues = indices.map((i) => xValues[i]) - yValues = indices.map((i) => yValues[i]) - } + const xValues = rankPlotInfo.xvalues + const yValues = rankPlotInfo.yvalues const plotData: Partial[] = [ { From 571bce66ebb37d5e8a7f0f3e34b4662347b4366e Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 5 Nov 2023 17:46:45 +0900 Subject: [PATCH 15/99] Change the definition of RankPlotInfo --- optuna_dashboard/ts/components/GraphRank.tsx | 17 +++-------------- 1 file changed, 3 insertions(+), 14 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index dc0a3842..63d2dec5 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -24,7 +24,6 @@ interface RankPlotInfo { ytype: plotly.AxisType xvalues: (string | number)[] yvalues: (string | number)[] - zvalues: number[] colors: number[] is_feasible: boolean[] hovertext: string[] @@ -170,15 +169,9 @@ const getRankPlotInfo = ( const colors = getColors(zValues) if (xAxis.isCat && !yAxis.isCat) { - const xIndices: number[] = Array.from(Array(xValues.length).keys()).sort( - (a, b) => - xValues[a] - .toString() - .toLowerCase() - .localeCompare(xValues[b].toString().toLowerCase()) - ) - xValues = xIndices.map((i) => xValues[i]) - yValues = xIndices.map((i) => yValues[i]) + const indices = xAxis.indices + xValues = indices.map((i: number) => xValues[i]) + yValues = indices.map((i) => yValues[i]) } if (!xAxis.isCat && yAxis.isCat) { const yIndices: number[] = Array.from(Array(yValues.length).keys()).sort( @@ -218,7 +211,6 @@ const getRankPlotInfo = ( ytype: yAxis.isCat ? "category" : yAxis.isLog ? "log" : "linear", xvalues: xValues, yvalues: yValues, - zvalues: zValues, colors, is_feasible: isFeasible, hovertext, @@ -285,9 +277,6 @@ const plotRank = (rankPlotInfo: RankPlotInfo | null, mode: string) => { template: mode === "dark" ? plotlyDarkTemplate : {}, } - const xValues = rankPlotInfo.xvalues - const yValues = rankPlotInfo.yvalues - const plotData: Partial[] = [ { type: "scatter", From e60663ff24447d9a749e616fb34994790ac2f222 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 12 Nov 2023 21:34:46 +0900 Subject: [PATCH 16/99] Follow review comments --- optuna_dashboard/ts/components/GraphRank.tsx | 28 +++++++++++--------- 1 file changed, 16 insertions(+), 12 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index 63d2dec5..56693e5d 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -153,9 +153,9 @@ const getRankPlotInfo = ( const isFeasible: boolean[] = [] const hovertext: string[] = [] filteredTrials.forEach((trial, i) => { - if (xAxis.values[i] && yAxis.values[i] && trial.values) { - const xValue = xAxis.values[i] as string | number - const yValue = yAxis.values[i] as string | number + const xValue = xAxis.values[i] + const yValue = yAxis.values[i] + if (xValue && yValue && trial.values) { xValues.push(xValue) yValues.push(yValue) const zValue = Number(trial.values[objectiveId]) @@ -169,22 +169,26 @@ const getRankPlotInfo = ( const colors = getColors(zValues) if (xAxis.isCat && !yAxis.isCat) { - const indices = xAxis.indices - xValues = indices.map((i: number) => xValues[i]) + const indices: number[] = Array.from(Array(xValues.length).keys()).sort( + (a, b) => + xValues[a] + .toString() + .toLowerCase() + .localeCompare(xValues[b].toString().toLowerCase()) + ) + xValues = indices.map((i) => xValues[i]) yValues = indices.map((i) => yValues[i]) - } - if (!xAxis.isCat && yAxis.isCat) { - const yIndices: number[] = Array.from(Array(yValues.length).keys()).sort( + } else if (!xAxis.isCat && yAxis.isCat) { + const indices: number[] = Array.from(Array(yValues.length).keys()).sort( (a, b) => yValues[a] .toString() .toLowerCase() .localeCompare(yValues[b].toString().toLowerCase()) ) - xValues = yIndices.map((i) => xValues[i]) - yValues = yIndices.map((i) => yValues[i]) - } - if (xAxis.isCat && yAxis.isCat) { + xValues = indices.map((i) => xValues[i]) + yValues = indices.map((i) => yValues[i]) + } else if (xAxis.isCat && yAxis.isCat) { const indices: number[] = Array.from(Array(xValues.length).keys()).sort( (a, b) => { const xComp = xValues[a] From 06695125afeac0d57fd8a9968aa031cba6bd6e41 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 12 Nov 2023 22:21:30 +0900 Subject: [PATCH 17/99] Introduce eqeqeq and fix some lines --- .eslintrc.js | 3 +- optuna_dashboard/ts/action.ts | 2 +- optuna_dashboard/ts/components/DataGrid.tsx | 2 +- .../ts/components/GraphIntermediateValues.tsx | 2 +- .../ts/components/GraphParallelCoordinate.tsx | 2 +- .../ts/components/StudyDetail.tsx | 2 +- .../ts/components/StudyHistory.tsx | 2 +- .../ts/components/TrialFormWidgets.tsx | 12 +- optuna_dashboard/ts/components/TrialList.tsx | 2 +- optuna_dashboard/ts/components/TrialTable.tsx | 2 +- optuna_dashboard/ts/dominatedTrials.ts | 2 +- optuna_dashboard/ts/state.ts | 2 +- package-lock.json | 1577 +++++++++-------- package.json | 6 +- standalone_app/src/components/DataGrid.tsx | 2 +- .../src/components/PlotIntermediateValues.tsx | 2 +- standalone_app/src/components/TrialTable.tsx | 2 +- standalone_app/src/sqlite3.ts | 8 +- 18 files changed, 840 insertions(+), 792 deletions(-) diff --git a/.eslintrc.js b/.eslintrc.js index 5b390797..1c8f2bb5 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -5,7 +5,8 @@ module.exports = { '@typescript-eslint', ], rules: { - "@typescript-eslint/ban-ts-comment": "off" + "@typescript-eslint/ban-ts-comment": "off", + "eqeqeq": ["error", "smart"], }, extends: [ 'eslint:recommended', diff --git a/optuna_dashboard/ts/action.ts b/optuna_dashboard/ts/action.ts index 41afdadb..ee3bba15 100644 --- a/optuna_dashboard/ts/action.ts +++ b/optuna_dashboard/ts/action.ts @@ -154,7 +154,7 @@ export const actionCreator = () => { currentValue > bestValue ) { newStudy.best_trials = [newTrial] - } else if (currentValue == bestValue) { + } else if (currentValue === bestValue) { newStudy.best_trials = [...newStudy.best_trials, newTrial] } } diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 24e45dc7..2855f1f7 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -358,7 +358,7 @@ function stableSort( const stabilizedThis = array.map((el, index) => [el, index] as [T, number]) stabilizedThis.sort((a, b) => { if (less) { - const ascending = order == "asc" + const ascending = order === "asc" const result = ascending ? -less(a[0], b[0], ascending) : less(a[0], b[0], ascending) diff --git a/optuna_dashboard/ts/components/GraphIntermediateValues.tsx b/optuna_dashboard/ts/components/GraphIntermediateValues.tsx index 2d86e464..a06832d9 100644 --- a/optuna_dashboard/ts/components/GraphIntermediateValues.tsx +++ b/optuna_dashboard/ts/components/GraphIntermediateValues.tsx @@ -78,7 +78,7 @@ const plotIntermediateValue = ( t.state === "Pruned" && t.values && t.values.length > 0) || - t.state == "Running" + t.state === "Running" ) const plotData: Partial[] = filteredTrials.map((trial) => { const values = trial.intermediate_values.filter( diff --git a/optuna_dashboard/ts/components/GraphParallelCoordinate.tsx b/optuna_dashboard/ts/components/GraphParallelCoordinate.tsx index 71f2b32d..b991ef94 100644 --- a/optuna_dashboard/ts/components/GraphParallelCoordinate.tsx +++ b/optuna_dashboard/ts/components/GraphParallelCoordinate.tsx @@ -164,7 +164,7 @@ const plotCoordinate = ( return truncated .split("") .map((c, i) => { - return (i + 1) % breakLength == 0 ? c + "
" : c + return (i + 1) % breakLength === 0 ? c + "
" : c }) .join("") } diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index 663ff399..70550465 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -192,7 +192,7 @@ export const StudyDetail: FC<{ ) - } else if (page == "preferenceHistory") { + } else if (page === "preferenceHistory") { content = } diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx index 907acd1a..e94a17ba 100644 --- a/optuna_dashboard/ts/components/StudyHistory.tsx +++ b/optuna_dashboard/ts/components/StudyHistory.tsx @@ -105,7 +105,7 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { {studyDetail !== null && - studyDetail.directions.length == 1 && + studyDetail.directions.length === 1 && studyDetail.has_intermediate_values ? ( { - if (formWidgets.output_type == "objective") { + if (formWidgets.output_type === "objective") { if (objectiveNames.at(i) !== undefined) { return objectiveNames[i] } - return directions.length == 1 ? "Objective" : `Objective ${i}` - } else if (formWidgets.output_type == "user_attr") { + return directions.length === 1 ? "Objective" : `Objective ${i}` + } else if (formWidgets.output_type === "user_attr") { if (widget.type !== "user_attr" && widget.user_attr_key !== undefined) { return widget.user_attr_key } @@ -118,13 +118,13 @@ const UpdatableFormWidgets: FC<{ const handleSubmit = (e: React.MouseEvent): void => { e.preventDefault() const values = widgetStates.map((ws) => ws.value) - if (formWidgets.output_type == "objective") { + if (formWidgets.output_type === "objective") { const filtered = values.filter((v): v is number => v !== null) if (filtered.length !== formWidgets.widgets.length) { return } action.makeTrialComplete(trial.study_id, trial.trial_id, filtered) - } else if (formWidgets.output_type == "user_attr") { + } else if (formWidgets.output_type === "user_attr") { const user_attrs = Object.fromEntries( formWidgets.widgets.map((widget, i) => [ widget.user_attr_key, @@ -433,7 +433,7 @@ const ReadonlyFormWidgets: FC<{ max={widget.max} step={widget.step} marks={ - widget.labels === null || widget.labels.length == 0 + widget.labels === null || widget.labels.length === 0 ? true : widget.labels } diff --git a/optuna_dashboard/ts/components/TrialList.tsx b/optuna_dashboard/ts/components/TrialList.tsx index 6922003a..cfde6536 100644 --- a/optuna_dashboard/ts/components/TrialList.tsx +++ b/optuna_dashboard/ts/components/TrialList.tsx @@ -119,7 +119,7 @@ const useIsBestTrial = ( return useMemo(() => { const bestTrialIDs = studyDetail?.best_trials.map((t) => t.trial_id) || [] return (trialId: number): boolean => - bestTrialIDs.findIndex((a) => a === trialId) != -1 + bestTrialIDs.findIndex((a) => a === trialId) !== -1 }, [studyDetail]) } diff --git a/optuna_dashboard/ts/components/TrialTable.tsx b/optuna_dashboard/ts/components/TrialTable.tsx index c708f6ee..0410818e 100644 --- a/optuna_dashboard/ts/components/TrialTable.tsx +++ b/optuna_dashboard/ts/components/TrialTable.tsx @@ -23,7 +23,7 @@ export const TrialTable: FC<{ toCellValue: (i) => trials[i].state.toString(), }, ] - if (studyDetail === null || studyDetail.directions.length == 1) { + if (studyDetail === null || studyDetail.directions.length === 1) { columns.push({ field: "values", label: "Value", diff --git a/optuna_dashboard/ts/dominatedTrials.ts b/optuna_dashboard/ts/dominatedTrials.ts index 06afb15f..b1571775 100644 --- a/optuna_dashboard/ts/dominatedTrials.ts +++ b/optuna_dashboard/ts/dominatedTrials.ts @@ -27,7 +27,7 @@ export const getDominatedTrials = ( const dominatedTrials: boolean[] = [] normalizedValues.forEach((values0: number[], i: number) => { const dominated = normalizedValues.some((values1: number[], j: number) => { - if (i === j || values0.every((v, i) => v == values1[i])) { + if (i === j || values0.every((v, i) => v === values1[i])) { return false } return values0.every((value0: number, k: number) => { diff --git a/optuna_dashboard/ts/state.ts b/optuna_dashboard/ts/state.ts index 3c6f0fd7..94e8d2d7 100644 --- a/optuna_dashboard/ts/state.ts +++ b/optuna_dashboard/ts/state.ts @@ -63,7 +63,7 @@ export const useStudyDetailValue = (studyId: number): StudyDetail | null => { export const useStudySummaryValue = (studyId: number): StudySummary | null => { const studySummaries = useRecoilValue(studySummariesState) - return studySummaries.find((s) => s.study_id == studyId) || null + return studySummaries.find((s) => s.study_id === studyId) || null } export const useTrialUpdatingValue = (trialId: number): boolean => { diff --git a/package-lock.json b/package-lock.json index 0561a75a..8aae1fb2 100644 --- a/package-lock.json +++ b/package-lock.json @@ -43,12 +43,12 @@ "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "@types/react-syntax-highlighter": "^15.5.5", - "@typescript-eslint/eslint-plugin": "^4.26.1", - "@typescript-eslint/parser": "^4.26.1", + "@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": "^7.28.0", + "eslint": "^8.53.0", "jest": "^29.2.1", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "^29.3.1", @@ -61,6 +61,15 @@ "webpack-cli": "^4.9.2" } }, + "node_modules/@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -1934,30 +1943,63 @@ "node": ">=12" } }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true, + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + } + }, "node_modules/@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1969,13 +2011,16 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/@eslint/eslintrc/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", + "node_modules/@eslint/eslintrc/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, - "engines": { - "node": ">= 4" + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, "node_modules/@eslint/eslintrc/node_modules/type-fest": { @@ -1990,24 +2035,46 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/@eslint/js": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "dev": true, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + } + }, "node_modules/@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "dependencies": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { @@ -4276,9 +4343,9 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "node_modules/@types/katex": { @@ -4409,6 +4476,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "node_modules/@types/semver": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "dev": true + }, "node_modules/@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -4470,30 +4543,33 @@ "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", + "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/type-utils": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^4.0.0", - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -4502,9 +4578,9 @@ } }, "node_modules/@typescript-eslint/eslint-plugin/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4516,50 +4592,27 @@ "node": ">=10" } }, - "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "dev": true, - "dependencies": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "*" - } - }, "node_modules/@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^5.0.0 || ^6.0.0 || ^7.0.0" + "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -4568,29 +4621,56 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/type-utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", + "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "dev": true, + "dependencies": { + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, "node_modules/@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", "dev": true, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -4598,21 +4678,21 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", @@ -4625,9 +4705,49 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", + "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "dev": true, + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "semver": "^7.5.4" + }, + "engines": { + "node": "^16.0.0 || >=18.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^7.0.0 || ^8.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "dependencies": { "lru-cache": "^6.0.0" @@ -4640,22 +4760,28 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "6.10.0", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^8.10.0 || ^10.13.0 || >=11.10.1" + "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "node_modules/@use-gesture/core": { "version": "10.2.27", "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.27.tgz", @@ -4983,15 +5109,6 @@ "ajv": "^6.9.1" } }, - "node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -5067,15 +5184,6 @@ "node": ">=8" } }, - "node_modules/astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -6083,18 +6191,6 @@ "node": ">=10.13.0" } }, - "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "dependencies": { - "ansi-colors": "^4.1.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -6623,57 +6719,55 @@ } }, "node_modules/eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "dependencies": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -6692,40 +6786,16 @@ "node": ">=8.0.0" } }, - "node_modules/eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^2.0.0" - }, "engines": { - "node": "^10.0.0 || ^12.0.0 || >= 14.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" - }, - "peerDependencies": { - "eslint": ">=5" - } - }, - "node_modules/eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true, - "engines": { - "node": ">=10" - } - }, - "node_modules/eslint/node_modules/@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "dependencies": { - "@babel/highlight": "^7.10.4" + "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/node_modules/ansi-styles": { @@ -6743,6 +6813,12 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, + "node_modules/eslint/node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "node_modules/eslint/node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -6789,34 +6865,51 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/eslint/node_modules/eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "dependencies": { - "eslint-visitor-keys": "^1.1.0" + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" }, "engines": { - "node": ">=6" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { - "url": "https://github.com/sponsors/mysticatea" + "url": "https://opencollective.com/eslint" } }, - "node_modules/eslint/node_modules/eslint-utils/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "engines": { - "node": ">=4" + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "dependencies": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint/node_modules/globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -6837,28 +6930,61 @@ "node": ">=8" } }, - "node_modules/eslint/node_modules/ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true, - "engines": { - "node": ">= 4" - } - }, - "node_modules/eslint/node_modules/semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "node_modules/eslint/node_modules/js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "dependencies": { - "lru-cache": "^6.0.0" + "argparse": "^2.0.1" }, "bin": { - "semver": "bin/semver.js" + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/eslint/node_modules/locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "dependencies": { + "p-locate": "^5.0.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/eslint/node_modules/p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "dependencies": { + "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint/node_modules/supports-color": { @@ -6894,26 +7020,32 @@ } }, "node_modules/espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "dependencies": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "node_modules/espree/node_modules/acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true, + "bin": { + "acorn": "bin/acorn" + }, "engines": { - "node": ">=4" + "node": ">=0.4.0" } }, "node_modules/esprima": { @@ -6929,9 +7061,9 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "dependencies": { "estraverse": "^5.1.0" @@ -7056,9 +7188,9 @@ "dev": true }, "node_modules/fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "dependencies": { "@nodelib/fs.stat": "^2.0.2", @@ -7071,6 +7203,18 @@ "node": ">=8.6.0" } }, + "node_modules/fast-glob/node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", @@ -7092,9 +7236,9 @@ } }, "node_modules/fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "dependencies": { "reusify": "^1.0.4" @@ -7169,22 +7313,23 @@ } }, "node_modules/flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "dependencies": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=12.0.0" } }, "node_modules/flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "node_modules/follow-redirects": { @@ -7261,12 +7406,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "node_modules/functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "node_modules/functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -7350,15 +7489,15 @@ } }, "node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "dependencies": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" }, "engines": { - "node": ">= 6" + "node": ">=10.13.0" } }, "node_modules/glob-to-regexp": { @@ -7427,6 +7566,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "node_modules/hamt_plus": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", @@ -7821,9 +7966,9 @@ } }, "node_modules/ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true, "engines": { "node": ">= 4" @@ -8123,6 +8268,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -10939,6 +11093,12 @@ "node": ">=4" } }, + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -10968,6 +11128,15 @@ "node": ">=6" } }, + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "dependencies": { + "json-buffer": "3.0.1" + } + }, "node_modules/kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -11096,12 +11265,6 @@ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" }, - "node_modules/lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -12317,17 +12480,17 @@ } }, "node_modules/optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "dependencies": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" }, "engines": { "node": ">= 0.8.0" @@ -12687,15 +12850,6 @@ "node": ">=6" } }, - "node_modules/progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -13082,18 +13236,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/mysticatea" - } - }, "node_modules/regexpu-core": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", @@ -13628,56 +13770,6 @@ "node": ">=8" } }, - "node_modules/slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/slice-ansi?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/slice-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/slice-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -13917,44 +14009,6 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, - "node_modules/table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "dependencies": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "engines": { - "node": ">=10.0.0" - } - }, - "node_modules/table/node_modules/ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/table/node_modules/json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, "node_modules/tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -14201,6 +14255,18 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "engines": { + "node": ">=16.13.0" + }, + "peerDependencies": { + "typescript": ">=4.2.0" + } + }, "node_modules/ts-jest": { "version": "29.0.3", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", @@ -14363,27 +14429,6 @@ "node": ">=8" } }, - "node_modules/tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "node_modules/tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "dependencies": { - "tslib": "^1.8.1" - }, - "engines": { - "node": ">= 6" - }, - "peerDependencies": { - "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" - } - }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -14681,12 +14726,6 @@ "node": ">=6" } }, - "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "node_modules/v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", @@ -15304,6 +15343,12 @@ } }, "dependencies": { + "@aashutoshrathi/word-wrap": { + "version": "1.2.6", + "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz", + "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==", + "dev": true + }, "@ampproject/remapping": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", @@ -16624,37 +16669,61 @@ "dev": true, "optional": true }, + "@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "requires": { + "eslint-visitor-keys": "^3.3.0" + } + }, + "@eslint-community/regexpp": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", + "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "dev": true + }, "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.3.tgz", + "integrity": "sha512-yZzuIG+jnVu6hNSzFEN07e8BxF3uAzYtQb6uDkaYZLo6oYZDCq454c5kB8zxnzfCYyP4MIuyBn10L0DqwujTmA==", "dev": true, "requires": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "dependencies": { + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "requires": { "type-fest": "^0.20.2" } }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "dev": true, + "requires": { + "argparse": "^2.0.1" + } }, "type-fest": { "version": "0.20.2", @@ -16664,21 +16733,33 @@ } } }, + "@eslint/js": { + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.53.0.tgz", + "integrity": "sha512-Kn7K8dx/5U6+cT1yEhpX1w4PCSg0M+XyRILPgvwcEBjerFWCwQj5sbr3/VmxqV0JGHCBCzyd6LxypEuehypY1w==", + "dev": true + }, "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "version": "0.11.13", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.13.tgz", + "integrity": "sha512-JSBDMiDKSzQVngfRjOdFXgFfklaXI4K9nLF49Auh21lmBWRLIK3+xTErTWD4KU54pb6coM6ESE7Awz/FNU3zgQ==", "dev": true, "requires": { - "@humanwhocodes/object-schema": "^1.2.0", + "@humanwhocodes/object-schema": "^2.0.1", "debug": "^4.1.1", - "minimatch": "^3.0.4" + "minimatch": "^3.0.5" } }, + "@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true + }, "@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.1.tgz", + "integrity": "sha512-dvuCeX5fC9dXgJn9t+X5atfmgQAzUOWqS1254Gh0m6i8wKd10ebXkfNKiRK+1GWi/yTvvLDHpoxLr0xxxeslWw==", "dev": true }, "@istanbuljs/load-nyc-config": { @@ -18333,9 +18414,9 @@ } }, "@types/json-schema": { - "version": "7.0.11", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.11.tgz", - "integrity": "sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/katex": { @@ -18468,6 +18549,12 @@ "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.2.tgz", "integrity": "sha512-hppQEBDmlwhFAXKJX2KnWLYu5yMfi91yazPb2l+lbJiwW+wdo1gNeRA+3RgNSO39WYX2euey41KEwnqesU2Jew==" }, + "@types/semver": { + "version": "7.5.5", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.5.tgz", + "integrity": "sha512-+d+WYC1BxJ6yVOgUgzK8gWvp5qF8ssV5r4nsDcZWKRWcDQLQ619tvWAxJQYGgBrO1MnLJC7a5GtiYsAoQ47dJg==", + "dev": true + }, "@types/stack-utils": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.1.tgz", @@ -18529,25 +18616,28 @@ "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.33.0.tgz", - "integrity": "sha512-aINiAxGVdOl1eJyVjaWn/YcVAq4Gi/Yo35qHGCnqbWVz61g39D0h23veY/MA0rFFGfxK7TySg2uwDeNv+JgVpg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.10.0.tgz", + "integrity": "sha512-uoLj4g2OTL8rfUQVx2AFO1hp/zja1wABJq77P6IclQs6I/m9GLrm7jCdgzZkvWdDCQf1uEvoa8s8CupsgWQgVg==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.33.0", - "@typescript-eslint/scope-manager": "4.33.0", - "debug": "^4.3.1", - "functional-red-black-tree": "^1.0.1", - "ignore": "^5.1.8", - "regexpp": "^3.1.0", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@eslint-community/regexpp": "^4.5.1", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/type-utils": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.4", + "natural-compare": "^1.4.0", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -18555,67 +18645,92 @@ } } }, - "@typescript-eslint/experimental-utils": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.33.0.tgz", - "integrity": "sha512-zeQjOoES5JFjTnAhI5QY7ZviczMzDptls15GFsI6jyUOq0kOf9+WonkhtlIhh0RgHRnqj5gdNxW5j1EvAyYg6Q==", - "dev": true, - "requires": { - "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^3.0.0" - } - }, "@typescript-eslint/parser": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.33.0.tgz", - "integrity": "sha512-ZohdsbXadjGBSK0/r+d87X0SBmKzOq4/S5nzK6SBgJspFo9/CUDJ7hjayuze+JK7CZQLDMroqytp7pOcFKTxZA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.10.0.tgz", + "integrity": "sha512-+sZwIj+s+io9ozSxIWbNB5873OSdfeBEH/FR0re14WLI6BaKuSOnnwCJ2foUiu8uXf4dRp1UqHP0vrZ1zXGrog==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.33.0", - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/typescript-estree": "4.33.0", - "debug": "^4.3.1" + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.33.0.tgz", - "integrity": "sha512-5IfJHpgTsTZuONKbODctL4kKuQje/bzBRkwHE8UOZ4f89Zeddg+EGZs8PD8NcN4LdM3ygHWYB3ukPAYjvl/qbQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.10.0.tgz", + "integrity": "sha512-TN/plV7dzqqC2iPNf1KrxozDgZs53Gfgg5ZHyw8erd6jd5Ta/JIEcdCheXFt9b1NYb93a1wmIIVW/2gLkombDg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0" + } + }, + "@typescript-eslint/type-utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.10.0.tgz", + "integrity": "sha512-wYpPs3hgTFblMYwbYWPT3eZtaDOjbLyIYuqpwuLBBqhLiuvJ+9sEp2gNRJEtR5N/c9G1uTtQQL5AhV0fEPJYcg==", + "dev": true, + "requires": { + "@typescript-eslint/typescript-estree": "6.10.0", + "@typescript-eslint/utils": "6.10.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.0.1" } }, "@typescript-eslint/types": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.33.0.tgz", - "integrity": "sha512-zKp7CjQzLQImXEpLt2BUw1tvOMPfNoTAfb8l51evhYbOEEzdWyQNmHWWGPR6hwKJDAi+1VXSBmnhL9kyVTTOuQ==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.10.0.tgz", + "integrity": "sha512-36Fq1PWh9dusgo3vH7qmQAj5/AZqARky1Wi6WpINxB6SkQdY5vQoT2/7rW7uBIsPDcvvGCLi4r10p0OJ7ITAeg==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.33.0.tgz", - "integrity": "sha512-rkWRY1MPFzjwnEVHsxGemDzqqddw2QbTJlICPD9p9I9LfsO8fdmfQPOX3uKfUaGRDFJbfrtm/sXhVXN4E+bzCA==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.10.0.tgz", + "integrity": "sha512-ek0Eyuy6P15LJVeghbWhSrBCj/vJpPXXR+EpaRZqou7achUWL8IdYnMSC5WHAeTWswYQuP2hAZgij/bC9fanBg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.33.0", - "@typescript-eslint/visitor-keys": "4.33.0", - "debug": "^4.3.1", - "globby": "^11.0.3", - "is-glob": "^4.0.1", - "semver": "^7.3.5", - "tsutils": "^3.21.0" + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/visitor-keys": "6.10.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.5.4", + "ts-api-utils": "^1.0.1" }, "dependencies": { "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "dev": true, + "requires": { + "lru-cache": "^6.0.0" + } + } + } + }, + "@typescript-eslint/utils": { + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.10.0.tgz", + "integrity": "sha512-v+pJ1/RcVyRc0o4wAGux9x42RHmAjIGzPRo538Z8M1tVx6HOnoQBCX/NoadHQlZeC+QO2yr4nNSFWOoraZCAyg==", + "dev": true, + "requires": { + "@eslint-community/eslint-utils": "^4.4.0", + "@types/json-schema": "^7.0.12", + "@types/semver": "^7.5.0", + "@typescript-eslint/scope-manager": "6.10.0", + "@typescript-eslint/types": "6.10.0", + "@typescript-eslint/typescript-estree": "6.10.0", + "semver": "^7.5.4" + }, + "dependencies": { + "semver": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", + "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", "dev": true, "requires": { "lru-cache": "^6.0.0" @@ -18624,15 +18739,21 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.33.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.33.0.tgz", - "integrity": "sha512-uqi/2aSz9g2ftcHWf8uLPJA70rUv6yuMW5Bohw+bwcuzaxQIHaKFZCKGoGXIrc9vkTJ3+0txM73K0Hq3d5wgIg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.10.0.tgz", + "integrity": "sha512-xMGluxQIEtOM7bqFCo+rCMh5fqI+ZxV5RUUOa29iVPz1OgCZrtc7rFnz5cLUazlkPKYqX+75iuDq7m0HQ48nCg==", "dev": true, "requires": { - "@typescript-eslint/types": "4.33.0", - "eslint-visitor-keys": "^2.0.0" + "@typescript-eslint/types": "6.10.0", + "eslint-visitor-keys": "^3.4.1" } }, + "@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true + }, "@use-gesture/core": { "version": "10.2.27", "resolved": "https://registry.npmjs.org/@use-gesture/core/-/core-10.2.27.tgz", @@ -18914,12 +19035,6 @@ "dev": true, "requires": {} }, - "ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true - }, "ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", @@ -18977,12 +19092,6 @@ "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true - }, "asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", @@ -19733,15 +19842,6 @@ "tapable": "^2.2.0" } }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, "entities": { "version": "4.4.0", "resolved": "https://registry.npmjs.org/entities/-/entities-4.4.0.tgz", @@ -20034,62 +20134,51 @@ } }, "eslint": { - "version": "7.32.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", - "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "version": "8.53.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.53.0.tgz", + "integrity": "sha512-N4VuiPjXDUa4xVeV/GC/RV3hQW9Nw+Y463lkWaKKXKYMvmRiRDAtfpuPFLN+E1/6ZhyR8J2ig+eVREnYgUsiag==", "dev": true, "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.3", + "@eslint/js": "8.53.0", + "@humanwhocodes/config-array": "^0.11.13", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" }, "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" - } - }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -20099,6 +20188,12 @@ "color-convert": "^2.0.1" } }, + "argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, "chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", @@ -20130,27 +20225,36 @@ "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true }, - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", + "eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + } + }, + "estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true + }, + "find-up": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", + "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "dev": true, + "requires": { + "locate-path": "^6.0.0", + "path-exists": "^4.0.0" } }, "globals": { - "version": "13.18.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.18.0.tgz", - "integrity": "sha512-/mR4KI8Ps2spmoc0Ulu9L7agOF0du1CZNQ3dke8yItYlyKNmGrkONemBbd6V8UTc1Wgcqn21t3WYB7dbRmh6/A==", + "version": "13.23.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.23.0.tgz", + "integrity": "sha512-XAmF0RjlrjY23MA51q3HltdlGxUpXPvg0GioKiD9X6HD28iMjo2dKC8Vqwm7lne4GNr78+RHTfliktR6ZH09wA==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -20162,19 +20266,40 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - }, - "semver": { - "version": "7.3.8", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.8.tgz", - "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==", + "js-yaml": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", + "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", "dev": true, "requires": { - "lru-cache": "^6.0.0" + "argparse": "^2.0.1" + } + }, + "locate-path": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", + "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "dev": true, + "requires": { + "p-locate": "^5.0.0" + } + }, + "p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "requires": { + "yocto-queue": "^0.1.0" + } + }, + "p-locate": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", + "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "dev": true, + "requires": { + "p-limit": "^3.0.2" } }, "supports-color": { @@ -20204,19 +20329,10 @@ "estraverse": "^4.1.1" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" - } - }, "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true }, "esm": { @@ -20225,20 +20341,20 @@ "integrity": "sha512-U1suiZ2oDVWv4zPO56S0NcR5QriEahGtdN2OR6FiOG4WJvcjBVFB0qI4+eKoWFH483PKGuLuu6V8Z4T5g63UVA==" }, "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" }, "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", + "acorn": { + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.2.tgz", + "integrity": "sha512-nc0Axzp/0FILLEVsm4fNwLCwMttvhEI263QtVPQcbpfZZ3ts0hLsZGOpE6czNlid7CJ9MlyH8reXkpsf3YUY4w==", "dev": true } } @@ -20249,9 +20365,9 @@ "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==" }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz", + "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==", "dev": true, "requires": { "estraverse": "^5.1.0" @@ -20347,9 +20463,9 @@ "dev": true }, "fast-glob": { - "version": "3.2.12", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.12.tgz", - "integrity": "sha512-DVj4CQIYYow0BlaelwK1pHl5n5cRSJfM60UA0zK891sVInoPri2Ekj7+e1CT3/3qxXenpI+nBBmQAcJPJgaj4w==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, "requires": { "@nodelib/fs.stat": "^2.0.2", @@ -20357,6 +20473,17 @@ "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "dependencies": { + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + } } }, "fast-json-stable-stringify": { @@ -20377,9 +20504,9 @@ "dev": true }, "fastq": { - "version": "1.14.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.14.0.tgz", - "integrity": "sha512-eR2D+V9/ExcbF9ls441yIuN6TI2ED1Y2ZcA5BmMtJsOkWOFRJQ0Jt0g1UwqXJJVAb+V+umH5Dfr8oh4EVP7VVg==", + "version": "1.15.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz", + "integrity": "sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==", "dev": true, "requires": { "reusify": "^1.0.4" @@ -20441,19 +20568,20 @@ } }, "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.1.1.tgz", + "integrity": "sha512-/qM2b3LUIaIgviBQovTLvijfyOQXPtSRnRK26ksj2J7rzPIecePUIpJsZ4T02Qg+xiAEKIs5K8dsHEd+VaKa/Q==", "dev": true, "requires": { - "flatted": "^3.1.0", + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" } }, "flatted": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.7.tgz", - "integrity": "sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==", + "version": "3.2.9", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.9.tgz", + "integrity": "sha512-36yxDn5H7OFZQla0/jFJmbIKTdZAQHngCedGxiMmpNfEZM0sdEeT+WczLQrjK6D7o2aiyLYDnkw0R3JK0Qv1RQ==", "dev": true }, "follow-redirects": { @@ -20503,12 +20631,6 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha512-dsKNQNdj6xA3T+QlADDA7mOSlX0qiMINjn0cgr+eGHGsbSHzTabcIogz2+p/iqP1Xs6EP/sS2SbqH+brGTbq0g==", - "dev": true - }, "functions-have-names": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/functions-have-names/-/functions-have-names-1.2.3.tgz", @@ -20565,12 +20687,12 @@ } }, "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "requires": { - "is-glob": "^4.0.1" + "is-glob": "^4.0.3" } }, "glob-to-regexp": { @@ -20625,6 +20747,12 @@ "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==", "dev": true }, + "graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true + }, "hamt_plus": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz", @@ -20912,9 +21040,9 @@ "requires": {} }, "ignore": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.1.tgz", - "integrity": "sha512-d2qQLzTJ9WxQftPAuEQpSPmKqzxePjzVbpAVv62AQ64NTL+wR4JkrVqR/LqFsFEUsHDAiId52mJteHDFuDkElA==", + "version": "5.2.4", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.2.4.tgz", + "integrity": "sha512-MAb38BcSbH0eHNBxn7ql2NH/kX33OkB3lZ1BNdh7ENeRChHTYsTvWrMubiIAMNS2llXEEgZ1MUOBtXChP3kaFQ==", "dev": true }, "import-fresh": { @@ -21103,6 +21231,12 @@ "has-tostringtag": "^1.0.0" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-obj": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-4.1.0.tgz", @@ -23209,6 +23343,12 @@ "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", "dev": true }, + "json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true + }, "json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -23232,6 +23372,15 @@ "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", "dev": true }, + "keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "requires": { + "json-buffer": "3.0.1" + } + }, "kind-of": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", @@ -23339,12 +23488,6 @@ "resolved": "https://registry.npmjs.org/lodash.pick/-/lodash.pick-4.4.0.tgz", "integrity": "sha512-hXt6Ul/5yWjfklSGvLQl8vM//l3FtyHZeuelpzK6mm99pNvN9yTDruNZPEJZD1oWrqo+izBmB7oUfWgcCX7s4Q==" }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", - "dev": true - }, "longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -24140,17 +24283,17 @@ } }, "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "version": "0.9.3", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz", + "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==", "dev": true, "requires": { + "@aashutoshrathi/word-wrap": "^1.2.3", "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", - "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "type-check": "^0.4.0" } }, "p-limit": { @@ -24391,12 +24534,6 @@ "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.29.0.tgz", "integrity": "sha512-Kx/1w86q/epKcmte75LNrEoT+lX8pBpavuAbvJWRXar7Hz8jrtF+e3vY751p0R8H9HdArwaCTNDDzHg/ScJK1Q==" }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, "prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", @@ -24680,12 +24817,6 @@ "functions-have-names": "^1.2.2" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, "regexpu-core": { "version": "5.2.2", "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-5.2.2.tgz", @@ -25072,43 +25203,6 @@ "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", - "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } - } - }, "source-list-map": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/source-list-map/-/source-list-map-2.0.1.tgz", @@ -25289,39 +25383,6 @@ "resolved": "https://registry.npmjs.org/symbol-tree/-/symbol-tree-3.2.4.tgz", "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==" }, - "table": { - "version": "6.8.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", - "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", - "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.3", - "strip-ansi": "^6.0.1" - }, - "dependencies": { - "ajv": { - "version": "8.11.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", - "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } - } - }, "tapable": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz", @@ -25496,6 +25557,13 @@ "resolved": "https://registry.npmjs.org/trough/-/trough-2.1.0.tgz", "integrity": "sha512-AqTiAOLcj85xS7vQ8QkAV41hPDIJ71XJB4RCUrzo/1GM2CQwhkJGaf9Hgr7BOugMRpgGUrqRg/DrBDl4H40+8g==" }, + "ts-api-utils": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.0.3.tgz", + "integrity": "sha512-wNMeqtMz5NtwpT/UZGY5alT+VoKdSsOOP/kqHFcUW1P/VRhH2wJ48+DN2WwUliNbQ976ETwDL0Ifd2VVvgonvg==", + "dev": true, + "requires": {} + }, "ts-jest": { "version": "29.0.3", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.0.3.tgz", @@ -25595,21 +25663,6 @@ } } }, - "tslib": { - "version": "1.14.1", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", - "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true - }, - "tsutils": { - "version": "3.21.0", - "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", - "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", - "dev": true, - "requires": { - "tslib": "^1.8.1" - } - }, "type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -25809,12 +25862,6 @@ } } }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, "v8-to-istanbul": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.0.1.tgz", diff --git a/package.json b/package.json index a675a1ce..406a7b07 100644 --- a/package.json +++ b/package.json @@ -52,12 +52,12 @@ "@types/react": "^18.0.26", "@types/react-dom": "^18.0.10", "@types/react-syntax-highlighter": "^15.5.5", - "@typescript-eslint/eslint-plugin": "^4.26.1", - "@typescript-eslint/parser": "^4.26.1", + "@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": "^7.28.0", + "eslint": "^8.53.0", "jest": "^29.2.1", "jest-canvas-mock": "^2.3.1", "jest-environment-jsdom": "^29.3.1", diff --git a/standalone_app/src/components/DataGrid.tsx b/standalone_app/src/components/DataGrid.tsx index 24e45dc7..2855f1f7 100644 --- a/standalone_app/src/components/DataGrid.tsx +++ b/standalone_app/src/components/DataGrid.tsx @@ -358,7 +358,7 @@ function stableSort( const stabilizedThis = array.map((el, index) => [el, index] as [T, number]) stabilizedThis.sort((a, b) => { if (less) { - const ascending = order == "asc" + const ascending = order === "asc" const result = ascending ? -less(a[0], b[0], ascending) : less(a[0], b[0], ascending) diff --git a/standalone_app/src/components/PlotIntermediateValues.tsx b/standalone_app/src/components/PlotIntermediateValues.tsx index 4feb0b33..8418cba6 100644 --- a/standalone_app/src/components/PlotIntermediateValues.tsx +++ b/standalone_app/src/components/PlotIntermediateValues.tsx @@ -78,7 +78,7 @@ const plotIntermediateValue = ( t.state === "Pruned" && t.values && t.values.length > 0) || - t.state == "Running" + t.state === "Running" ) const plotData: Partial[] = filteredTrials.map((trial) => { const values = trial.intermediate_values.filter( diff --git a/standalone_app/src/components/TrialTable.tsx b/standalone_app/src/components/TrialTable.tsx index 97a04f3c..d8b4f163 100644 --- a/standalone_app/src/components/TrialTable.tsx +++ b/standalone_app/src/components/TrialTable.tsx @@ -20,7 +20,7 @@ export const TrialTable: FC<{ }, ] - if (study === null || study.directions.length == 1) { + if (study === null || study.directions.length === 1) { columns.push({ field: "values", label: "Value", diff --git a/standalone_app/src/sqlite3.ts b/standalone_app/src/sqlite3.ts index 3a04c163..1fbcff87 100644 --- a/standalone_app/src/sqlite3.ts +++ b/standalone_app/src/sqlite3.ts @@ -64,7 +64,7 @@ const getSchemaVersion = (db: SQLite3DB): string => { const isSupportedSchema = (schemaVersion: string): boolean => { const lowestVersion = "v2.6.0.a" // supported: "v3.2.0.a", "v3.0.0.{a,b,c,d}", "v2.6.0.a" - if (schemaVersion == lowestVersion) return true + if (schemaVersion === lowestVersion) return true return isGreaterSchemaVersion(schemaVersion, lowestVersion) } @@ -80,7 +80,7 @@ const isGreaterSchemaVersion = ( const left = Number(leftVersion) const right = Number(rightVersion) - if (left == right) return leftSuffix > rightSuffix + if (left === right) return leftSuffix > rightSuffix return left > right } @@ -106,7 +106,7 @@ const getStudies = (db: SQLite3DB, schemaVersion: string): Study[] => { trials.forEach((trial) => { const userAttrs = getTrialUserAttributes(db, trial.trial_id) userAttrs.forEach((attr) => { - if (union_user_attrs.findIndex((s) => s.key === attr.key) == -1) { + if (union_user_attrs.findIndex((s) => s.key === attr.key) === -1) { union_user_attrs.push({ key: attr.key, sortable: false }) } }) @@ -116,7 +116,7 @@ const getStudies = (db: SQLite3DB, schemaVersion: string): Study[] => { params.forEach((param) => { param_names.add(param.name) if ( - union_search_space.findIndex((s) => s.name === param.name) == -1 + union_search_space.findIndex((s) => s.name === param.name) === -1 ) { union_search_space.push({ name: param.name }) } From aa4909015ae27e4785493d85eb9306673b46fe45 Mon Sep 17 00:00:00 2001 From: c-bata Date: Thu, 16 Nov 2023 11:06:31 +0900 Subject: [PATCH 18/99] Filter DataGrid rows by multiple conditions --- optuna_dashboard/ts/components/DataGrid.tsx | 222 +++++++++--------- optuna_dashboard/ts/components/TrialTable.tsx | 10 +- 2 files changed, 121 insertions(+), 111 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 41ecc4e4..64f5303b 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -10,12 +10,16 @@ import { TableSortLabel, Collapse, IconButton, - useTheme, + Menu, + MenuItem, } from "@mui/material" import { styled } from "@mui/system" import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown" import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp" -import { Clear } from "@mui/icons-material" +import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank" +import CheckBoxIcon from "@mui/icons-material/CheckBox" +import FilterListIcon from "@mui/icons-material/FilterList" +import ListItemIcon from "@mui/material/ListItemIcon" type Order = "asc" | "desc" @@ -29,14 +33,14 @@ interface DataGridColumn { label: string sortable?: boolean less?: (a: T, b: T, ascending: boolean) => number - filterable?: boolean + filterChoices?: string[] toCellValue?: (rowIndex: number) => string | React.ReactNode padding?: "normal" | "checkbox" | "none" } interface RowFilter { columnIdx: number - value: Value + values: Value[] } function DataGrid(props: { @@ -81,24 +85,13 @@ function DataGrid(props: { } // Filtering - const fieldAlreadyFiltered = (columnIdx: number): boolean => - filters.some((f) => f.columnIdx === columnIdx) - - const handleClickFilterCell = (columnIdx: number, value: Value) => { - if (fieldAlreadyFiltered(columnIdx)) { - return - } - const newFilters = [...filters, { columnIdx: columnIdx, value: value }] - setFilters(newFilters) - } - const filteredRows = rows.filter((row, rowIdx) => { if (defaultFilter !== undefined && defaultFilter(row)) { return false } return filters.length === 0 ? true - : filters.some((f) => { + : filters.every((f) => { if (columns.length <= f.columnIdx) { console.log( `columnIdx=${f.columnIdx} must be smaller than columns.length=${columns.length}` @@ -106,11 +99,11 @@ function DataGrid(props: { return true } const toCellValue = columns[f.columnIdx].toCellValue - if (toCellValue !== undefined) { - return toCellValue(rowIdx) === f.value - } - const field = columns[f.columnIdx].field - return row[field] === f.value + const value = + toCellValue !== undefined + ? toCellValue(rowIdx) + : row[columns[f.columnIdx].field] + return f.values.some((v) => v === value) }) }) @@ -137,21 +130,32 @@ function DataGrid(props: { {collapseBody ? : null} - {columns.map((column, columnIdx) => ( - - key={column.label} - column={column} - orderBy={orderBy === columnIdx ? order : null} - onOrderByChange={(direction: Order) => { - setOrder(direction) - setOrderBy(columnIdx) - }} - onFilterClear={() => { - setFilters(filters.filter((f) => f.columnIdx !== columnIdx)) - }} - filtered={fieldAlreadyFiltered(columnIdx)} - /> - ))} + {columns.map((column, columnIdx) => { + return ( + + key={columnIdx} + column={column} + order={orderBy === columnIdx ? order : null} + filter={ + filters.find((f) => f.columnIdx === columnIdx) || null + } + onOrderByChange={(direction: Order) => { + setOrder(direction) + setOrderBy(columnIdx) + }} + onFilterChange={(values: Value[]) => { + const newFilters = filters.filter( + (f) => f.columnIdx !== columnIdx + ) + newFilters.push({ + columnIdx: columnIdx, + values: values, + }) + setFilters(newFilters) + }} + /> + ) + })} @@ -163,7 +167,6 @@ function DataGrid(props: { keyField={keyField} collapseBody={collapseBody} key={`${row[keyField]}`} - handleClickFilterCell={handleClickFilterCell} /> ))} {emptyRows > 0 && ( @@ -187,70 +190,103 @@ function DataGrid(props: { ) } +const TableHeaderCellSpan = styled("span")({ + display: "inline-flex", +}) + +const HiddenSpan = styled("span")({ + border: 0, + clip: "rect(0 0 0 0)", + height: 1, + margin: -1, + overflow: "hidden", + padding: 0, + position: "absolute", + top: 20, + width: 1, +}) + function DataGridHeaderColumn(props: { column: DataGridColumn - orderBy: Order | null - onOrderByChange: (direction: Order) => void - filtered: boolean - onFilterClear: () => void + order: Order | null + onOrderByChange: (order: Order) => void + filter: RowFilter | null + onFilterChange: (values: Value[]) => void dense?: boolean }) { - const { column, orderBy, onOrderByChange, filtered, onFilterClear, dense } = + const { column, order, onOrderByChange, filter, onFilterChange, dense } = props + const [filterMenuAnchorEl, setFilterMenuAnchorEl] = + React.useState(null) + + const filterChoices = column.filterChoices - const HiddenSpan = styled("span")({ - border: 0, - clip: "rect(0 0 0 0)", - height: 1, - margin: -1, - overflow: "hidden", - padding: 0, - position: "absolute", - top: 20, - width: 1, - }) - const TableHeaderCellSpan = styled("span")({ - display: "inline-flex", - }) return ( {column.sortable ? ( { - if (orderBy === null) { - onOrderByChange("asc") - } else { - onOrderByChange(orderBy === "desc" ? "asc" : "desc") - } + onOrderByChange(order === "asc" ? "desc" : "asc") }} > {column.label} - {orderBy !== null ? ( + {order !== null ? ( - {orderBy === "desc" ? "sorted descending" : "sorted ascending"} + {order === "desc" ? "sorted descending" : "sorted ascending"} ) : null} ) : ( column.label )} - {column.filterable ? ( - { - onFilterClear() - }} - > - - + {filterChoices !== undefined ? ( + <> + { + setFilterMenuAnchorEl(e.currentTarget) + }} + > + + + { + setFilterMenuAnchorEl(null) + }} + > + {filterChoices.map((choice, i) => ( + { + const values = + filter === null + ? filterChoices.filter((v) => v !== choice) + : filter.values.some((v) => v === choice) + ? filter.values.filter((v) => v !== choice) + : [...filter.values, choice] + onFilterChange(values) + }} + > + + {!filter || !filter.values.every((v) => v !== choice) ? ( + + ) : ( + + )} + + {choice} + + ))} + + ) : null} @@ -263,24 +299,10 @@ function DataGridRow(props: { row: T keyField: keyof T collapseBody?: (rowIndex: number) => React.ReactNode - handleClickFilterCell: (columnIdx: number, value: Value) => void }) { - const { - columns, - rowIndex, - row, - keyField, - collapseBody, - handleClickFilterCell, - } = props + const { columns, rowIndex, row, keyField, collapseBody } = props const [open, setOpen] = React.useState(false) - const theme = useTheme() - const FilterableDiv = styled("div")({ - color: theme.palette.primary.main, - textDecoration: "underline", - cursor: "pointer", - }) return ( @@ -301,21 +323,7 @@ function DataGridRow(props: { : // TODO(c-bata): Avoid this implicit type conversion. (row[column.field] as number | string | null | undefined) - return column.filterable ? ( - { - const value = - column.toCellValue !== undefined - ? column.toCellValue(rowIndex) - : row[column.field] - handleClickFilterCell(columnIndex, value) - }} - > - {cellItem} - - ) : ( + return ( trials[i].state.toString(), }, @@ -97,7 +97,10 @@ export const TrialTable: FC<{ ) { studyDetail?.intersection_search_space.forEach((s) => { const sortable = s.distribution.type !== "CategoricalDistribution" - const filterable = s.distribution.type === "CategoricalDistribution" + const filterChoices = + s.distribution.type === "CategoricalDistribution" + ? s.distribution.choices.map((c) => c.value) + : undefined columns.push({ field: "params", label: `Param ${s.name}`, @@ -105,7 +108,7 @@ export const TrialTable: FC<{ trials[i].params.find((p) => p.name === s.name) ?.param_external_value || null, sortable: sortable, - filterable: filterable, + filterChoices: filterChoices, // eslint-disable-next-line @typescript-eslint/no-unused-vars less: (firstEl, secondEl, _): number => { const firstVal = firstEl.params.find( @@ -146,7 +149,6 @@ export const TrialTable: FC<{ trials[i].user_attrs.find((attr) => attr.key === attr_spec.key) ?.value || null, sortable: attr_spec.sortable, - filterable: !attr_spec.sortable, // eslint-disable-next-line @typescript-eslint/no-unused-vars less: (firstEl, secondEl, _): number => { const firstVal = firstEl.user_attrs.find( From 77d9fff7080d5e841243d59c826eecaaa4447c0a Mon Sep 17 00:00:00 2001 From: c-bata Date: Thu, 16 Nov 2023 15:38:33 +0900 Subject: [PATCH 19/99] Fix broken tests --- optuna_dashboard/ts/components/DataGrid.tsx | 2 +- typescript_tests/DataGrid.test.tsx | 47 ++------------------- 2 files changed, 4 insertions(+), 45 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index 64f5303b..faa7e75c 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -262,7 +262,7 @@ function DataGridHeaderColumn(props: { setFilterMenuAnchorEl(null) }} > - {filterChoices.map((choice, i) => ( + {filterChoices.map((choice) => ( { diff --git a/typescript_tests/DataGrid.test.tsx b/typescript_tests/DataGrid.test.tsx index 475e360a..6d13deb7 100644 --- a/typescript_tests/DataGrid.test.tsx +++ b/typescript_tests/DataGrid.test.tsx @@ -1,7 +1,7 @@ import React from "react" global.URL.createObjectURL = jest.fn() -import { cleanup, render, fireEvent } from "@testing-library/react" +import { cleanup, render } from "@testing-library/react" import { DataGrid, DataGridColumn, @@ -9,6 +9,7 @@ import { afterEach(cleanup) +// TODO(c-bata): Add tests to check filterChoices option it("Filter rows of DataGrid", () => { interface DummyAttribute { id: number @@ -23,7 +24,7 @@ it("Filter rows of DataGrid", () => { { id: 5, key: "foo", value: 3 }, ] const columns: DataGridColumn[] = [ - { field: "key", label: "Key", filterable: true }, + { field: "key", label: "Key" }, { field: "value", label: "Value", @@ -39,46 +40,4 @@ it("Filter rows of DataGrid", () => { /> ) expect(queryAllByText("bar").length).toBe(2) - - // Filter rows by "foo" - fireEvent.click(queryAllByText("foo")[0]) - expect(queryAllByText("foo").length).toBe(3) - expect(queryAllByText("bar").length).toBe(0) -}) - -it("Filter rows after sorted", () => { - interface DummyAttribute { - id: number - key: string - value: number - } - const dummyAttributes = [ - { id: 1, key: "foo", value: 4000 }, - { id: 2, key: "bar", value: 1000 }, - { id: 3, key: "bar", value: 2000 }, - { id: 4, key: "foo", value: 3000 }, - { id: 5, key: "foo", value: 5000 }, - ] - const columns: DataGridColumn[] = [ - { field: "key", label: "Key", filterable: true }, - { - field: "value", - label: "Value", - sortable: true, - }, - ] - - const { getByText, queryAllByText } = render( - - columns={columns} - rows={dummyAttributes} - keyField={"id"} - /> - ) - // Sort and filter rows - fireEvent.click(getByText("Value")) - fireEvent.click(queryAllByText("bar")[0]) - - expect(queryAllByText("1000").length).toBe(1) - expect(queryAllByText("2000").length).toBe(1) }) From 9d7e10759e7e9dcffc45939e0cfdae6d38fdf4fd Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 17 Nov 2023 14:53:56 +0900 Subject: [PATCH 20/99] Hide study artifact card when artifact is not enabled --- .../ts/components/StudyHistory.tsx | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx index dbe15427..bdfb225b 100644 --- a/optuna_dashboard/ts/components/StudyHistory.tsx +++ b/optuna_dashboard/ts/components/StudyHistory.tsx @@ -172,31 +172,31 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { - - - - - + + + - Study Artifacts - - {artifactEnabled && studyDetail !== null && ( + + Study Artifacts + - )} - - + + + - + )} ) } From 68e181e838e769fcfc7964add044d8cf9e636869 Mon Sep 17 00:00:00 2001 From: gen740 Date: Fri, 17 Nov 2023 16:53:00 +0900 Subject: [PATCH 21/99] Delete unnecessary dashboard_artifact system_attr deletion --- optuna_dashboard/artifact/_backend.py | 4 ---- 1 file changed, 4 deletions(-) diff --git a/optuna_dashboard/artifact/_backend.py b/optuna_dashboard/artifact/_backend.py index 55dc844c..9720124b 100644 --- a/optuna_dashboard/artifact/_backend.py +++ b/optuna_dashboard/artifact/_backend.py @@ -204,10 +204,6 @@ def register_artifact_route( return {"reason": "Cannot access to the artifacts."} artifact_store.remove(artifact_id) - # The artifact's metadata is stored in one of the following two locations: - storage.set_study_system_attr( - study_id, _dashboard_artifact_prefix(study_id) + artifact_id, json.dumps(None) - ) storage.set_study_system_attr( study_id, ARTIFACTS_ATTR_PREFIX + artifact_id, json.dumps(None) ) From bcf8728325934a4eb227f42c80a97613f64664d4 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Fri, 17 Nov 2023 17:55:05 +0900 Subject: [PATCH 22/99] Add tests for save_trial_user_attrs --- python_tests/test_api.py | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index ce78da47..8aa8fac2 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -220,6 +220,49 @@ class APITestCase(TestCase): assert study_detail["feedback_component_type"]["output_type"] == "artifact" assert study_detail["feedback_component_type"]["artifact_key"] == "image" + def test_save_trial_user_attrs(self) -> None: + study = optuna.create_study() + trials: list[optuna.Trial] = [] + for _ in range(2): + trial = study.ask() + trials.append(trial) + + request_body = { + "user_attrs": { + "number": 0, + }, + } + + app = create_app(study._storage) + status, _, _ = send_request( + app, + f"/api/trials/{trials[0]._trial_id}/user-attrs", + "POST", + content_type="application/json", + body=json.dumps(request_body), + ) + self.assertEqual(status, 204) + + assert study.trials[0].user_attrs == request_body["user_attrs"] + assert study.trials[1].user_attrs == {} + + + def test_save_trial_user_attrs_empty(self) -> None: + study = optuna.create_study() + trial = study.ask() + + app = create_app(study._storage) + status, _, _ = send_request( + app, + f"/api/trials/{trial._trial_id}/user-attrs", + "POST", + content_type="application/json", + body=json.dumps({}), + ) + self.assertEqual(status, 400) + assert study.trials[0].user_attrs == {} + + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_skip_trial(self) -> None: storage = optuna.storages.InMemoryStorage() From c1c6adc3a6318d56d4b6292878313309ee5575ae Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Fri, 17 Nov 2023 18:03:23 +0900 Subject: [PATCH 23/99] Remove unnecessary empty lines. --- python_tests/test_api.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 8aa8fac2..721bdb56 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -246,7 +246,6 @@ class APITestCase(TestCase): assert study.trials[0].user_attrs == request_body["user_attrs"] assert study.trials[1].user_attrs == {} - def test_save_trial_user_attrs_empty(self) -> None: study = optuna.create_study() trial = study.ask() @@ -262,7 +261,6 @@ class APITestCase(TestCase): self.assertEqual(status, 400) assert study.trials[0].user_attrs == {} - @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_skip_trial(self) -> None: storage = optuna.storages.InMemoryStorage() From 319448a7c167ceb7823f2c4c26ae9532ca6c723b Mon Sep 17 00:00:00 2001 From: Victoria A <52001888+adjeiv@users.noreply.github.com> Date: Sat, 18 Nov 2023 10:57:39 +0000 Subject: [PATCH 24/99] Tests, formatting --- optuna_dashboard/_app.py | 2 +- optuna_dashboard/_note.py | 16 ++++++-- python_tests/test_note.py | 77 +++++++++++++++++++++++++++------------ 3 files changed, 66 insertions(+), 29 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 9ce820b7..32e2a431 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -164,7 +164,7 @@ def create_app( if new_study_summary is None: response.status = 500 return {"reason": "Failed to load the new study"} - + note.transfer_notes(storage, src_study, dst_study) storage.delete_study(src_study._study_id) response.status = 201 diff --git a/optuna_dashboard/_note.py b/optuna_dashboard/_note.py index e510ddfd..6107d56c 100644 --- a/optuna_dashboard/_note.py +++ b/optuna_dashboard/_note.py @@ -109,6 +109,7 @@ def note_str_key_prefix(trial_id: Optional[int]) -> str: return prefix return f"dashboard:{trial_id}:note_str:" + def transfer_notes(storage: BaseStorage, src_study: optuna.Study, dst_study: optuna.Study) -> None: system_attrs = storage.get_study_system_attrs(study_id=src_study._study_id) @@ -120,11 +121,12 @@ def transfer_notes(storage: BaseStorage, src_study: optuna.Study, dst_study: opt # Transfer individual trial notes for src_trial, dst_trial in zip(src_study.get_trials(), dst_study.get_trials()): transfer(src_trial._trial_id, dst_trial._trial_id) - + # Transfer study note NO_SRC_TRIAL, NO_DST_TRIAL = None, None transfer(NO_SRC_TRIAL, NO_DST_TRIAL) + def get_note_from_system_attrs(system_attrs: dict[str, Any], trial_id: Optional[int]) -> NoteType: if note_ver_key(trial_id) not in system_attrs: return { @@ -146,7 +148,10 @@ def version_is_incremented( db_note_ver = system_attrs.get(note_ver_key(trial_id), 0) return req_note_ver == db_note_ver + 1 -def all_trial_notes(storage: BaseStorage, study_id: int, trial_id: Optional[int]) -> dict[str, str]: + +def all_trial_notes( + storage: BaseStorage, study_id: int, trial_id: Optional[int] +) -> dict[str, str]: all_note_attrs: dict[str, str] = { key: value for key, value in storage.get_study_system_attrs(study_id).items() @@ -154,6 +159,7 @@ def all_trial_notes(storage: BaseStorage, study_id: int, trial_id: Optional[int] } return all_note_attrs + def save_note_with_version( storage: BaseStorage, study_id: int, trial_id: Optional[int], ver: int, body: str ) -> None: @@ -169,19 +175,21 @@ def save_note_with_version( for i in range(len(attrs), len(all_note_attrs)): storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") + def delete_study_notes(storage: BaseStorage, study_id): - study = storage.get_study_name_from_id(study_id) for trial in storage.get_all_trials(study_id): delete_notes(storage, study_id, trial._trial_id) - + delete_notes(storage, study_id, None) + def delete_notes(storage: BaseStorage, study_id: int, trial_id: Optional[int]) -> None: all_note_attrs = all_trial_notes(storage, study_id, trial_id) for i in range(len(all_note_attrs)): storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") + def split_body(note_str: str, trial_id: Optional[int]) -> dict[str, str]: note_len = len(note_str) attrs = {} diff --git a/python_tests/test_note.py b/python_tests/test_note.py index 4997d16a..55c8f610 100644 --- a/python_tests/test_note.py +++ b/python_tests/test_note.py @@ -56,41 +56,70 @@ class NoteTestCase(TestCase): def test_delete_notes_trial(self) -> None: study = optuna.create_study() - trial_1 = study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) - trial_2 = study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) + trials = [ + study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) for _ in range(2) + ] storage = study._storage - for trial, body in [(trial_1, "version 1"), (trial_2, "version 2")]: - save_note(trial, body) + notes = ["trial 0", "trial 1"] + for trial, body in zip(trials, notes): + with self.subTest(body): + save_note(trial, body) - # first assert existence - actual = get_note(trial) - self.assertEqual(actual, body) + self.assertEqual(get_note(trial), body) - # delete - note.delete_notes(storage, study._study_id, trial._trial_id) + note.delete_notes(storage, study._study_id, trial._trial_id) - # assert deletion - actual = get_note(trial) - self.assertEqual(actual, "") + self.assertEqual(get_note(trial), "") + def test_delete_notes_study(self) -> None: + study = optuna.create_study() + trials = [ + study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) for _ in range(2) + ] + storage = study._storage + + notes = ["trial 0", "trial 1"] + for trial, body in zip(trials, notes): + with self.subTest(body): + save_note(trial, body) + + actual = get_note(trial) + self.assertEqual(actual, body) + + save_note(study, "Study note") + actual = get_note(study) + self.assertEqual(actual, "Study note") note.delete_study_notes(storage, study._study_id) - def test_delete_notes_study(self) -> None: - pass + for trial in trials: + self.assertEqual(get_note(trial), "") + self.assertEqual(get_note(study), "") def test_transfer_notes(self) -> None: - study = optuna.create_study() - trial_1 = study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) - trial_2 = study.ask({"x2": optuna.distributions.FloatDistribution(0, 10)}) - storage = study._storage + old_study = optuna.create_study() + old_trials = [ + old_study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) for _ in range(2) + ] + storage = old_study._storage - save_note(trial_1, "trial 1") - save_note(trial_2, "trial 2") + notes = ["trial 0", "trial 1"] + for trial, body in zip(old_trials, notes): + save_note(trial, body) + save_note(old_study, "Study") - new_study = optuna.create_study( - storage=storage, directions=study.directions - ) - note.transfer_notes(storage, study, new_study) + new_study = optuna.create_study(storage=storage, directions=old_study.directions) + new_study.add_trials(old_study.get_trials(deepcopy=False)) + note.transfer_notes(storage, old_study, new_study) + + for old_trial in old_trials: + self.assertEqual(get_note(old_trial), "") + self.assertEqual(get_note(old_study), "") + + system_attrs = new_study._storage.get_study_system_attrs(new_study._study_id) + for new_trial, body in zip(new_study.get_trials(), notes): + actual = note.get_note_from_system_attrs(system_attrs, new_trial._trial_id) + self.assertEqual(actual["body"], body) + self.assertEqual(get_note(new_study), "Study") From 11e38ea439077d5aa13390cbfc8c2823d869341d Mon Sep 17 00:00:00 2001 From: Victoria A <52001888+adjeiv@users.noreply.github.com> Date: Sat, 18 Nov 2023 11:30:55 +0000 Subject: [PATCH 25/99] Typing info --- optuna_dashboard/_note.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_note.py b/optuna_dashboard/_note.py index 6107d56c..57135211 100644 --- a/optuna_dashboard/_note.py +++ b/optuna_dashboard/_note.py @@ -176,7 +176,7 @@ def save_note_with_version( storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") -def delete_study_notes(storage: BaseStorage, study_id): +def delete_study_notes(storage: BaseStorage, study_id: int) -> None: for trial in storage.get_all_trials(study_id): delete_notes(storage, study_id, trial._trial_id) From ecf5ab80f7ed3a858b51640ec75e886dd06b828f Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 22 Nov 2023 11:48:22 +0900 Subject: [PATCH 26/99] Support numpy scalars in Trial.user_attrs --- .../_cached_extra_study_property.py | 11 ++++---- optuna_dashboard/_serializer.py | 2 ++ .../test_cached_extra_study_property.py | 23 ++++++++++++++-- python_tests/test_serializers.py | 27 +++++++++++++++++++ 4 files changed, 56 insertions(+), 7 deletions(-) diff --git a/optuna_dashboard/_cached_extra_study_property.py b/optuna_dashboard/_cached_extra_study_property.py index 2f27fa64..1e4fb1a7 100644 --- a/optuna_dashboard/_cached_extra_study_property.py +++ b/optuna_dashboard/_cached_extra_study_property.py @@ -2,12 +2,14 @@ from __future__ import annotations import copy import threading +from typing import Any from typing import List from typing import Optional from typing import Set from typing import Tuple from typing import TYPE_CHECKING +import numpy as np from optuna.distributions import BaseDistribution from optuna.trial import FrozenTrial from optuna.trial import TrialState @@ -84,12 +86,11 @@ class _CachedExtraStudyProperty: self._cursor = next_cursor + def _is_sortable_value(self, v: Any) -> bool: + return not isinstance(v, bool) and isinstance(v, (int, float, np.integer, np.floating)) + def _update_user_attrs(self, trial: FrozenTrial) -> None: - # TODO(c-bata): Support numpy-specific number types. - current_user_attrs = { - k: not isinstance(v, bool) and isinstance(v, (int, float)) - for k, v in trial.user_attrs.items() - } + current_user_attrs = {k: self._is_sortable_value(v) for k, v in trial.user_attrs.items()} for attr_name, current_is_sortable in current_user_attrs.items(): is_sortable = self._union_user_attrs.get(attr_name) if is_sortable is None: diff --git a/optuna_dashboard/_serializer.py b/optuna_dashboard/_serializer.py index b5a3b305..9b0cce64 100644 --- a/optuna_dashboard/_serializer.py +++ b/optuna_dashboard/_serializer.py @@ -104,6 +104,8 @@ def serialize_attrs(attrs: dict[str, Any]) -> list[Attribute]: value = "" elif isinstance(v, str): value = v + elif isinstance(v, (np.floating, np.integer)): + value = v.item() else: value = json.dumps(v) value = value[:MAX_ATTR_LENGTH] if len(value) > MAX_ATTR_LENGTH else value diff --git a/python_tests/test_cached_extra_study_property.py b/python_tests/test_cached_extra_study_property.py index c02006a7..dcd5bc5e 100644 --- a/python_tests/test_cached_extra_study_property.py +++ b/python_tests/test_cached_extra_study_property.py @@ -4,6 +4,7 @@ from typing import Any from unittest import TestCase import warnings +import numpy as np import optuna from optuna import create_trial from optuna.distributions import BaseDistribution @@ -254,11 +255,29 @@ class _CachedExtraStudyPropertyUserAttrs(TestCase): def test_infer_sortable(self) -> None: user_attrs_list: list[dict[str, Any]] = [ - {"a": 1, "b": 1, "c": 1, "d": "a", "e": 1, "f": True}, + { + "a": 1, + "b": 1, + "c": 1, + "d": "a", + "e": 1, + "f": True, + "g": np.float128(1.1), + "h": np.int64(2), + }, {"a": 2, "b": "a", "c": "a", "d": "a"}, {"a": 3, "b": None, "c": 3, "d": "a", "e": 3}, ] - expected = {"a": True, "b": False, "c": False, "d": False, "e": True, "f": False} + expected = { + "a": True, + "b": False, + "c": False, + "d": False, + "e": True, + "f": False, + "g": True, + "h": True, + } trials = [] for user_attrs in user_attrs_list: diff --git a/python_tests/test_serializers.py b/python_tests/test_serializers.py index 0d1546d2..8c64c5d8 100644 --- a/python_tests/test_serializers.py +++ b/python_tests/test_serializers.py @@ -2,6 +2,7 @@ from __future__ import annotations import sys +import numpy as np import optuna from optuna_dashboard._serializer import serialize_attrs from optuna_dashboard._serializer import serialize_study_detail @@ -25,6 +26,32 @@ def test_serialize_dict() -> None: assert len(serialized) <= 1 +def test_serialize_numpy_integer() -> None: + serialized = serialize_attrs( + { + "int8": np.int8(1), + "int16": np.int16(1), + "int32": np.int32(1), + "int64": np.int64(1), + } + ) + assert len(serialized) == 4 + assert all([v["value"] == 1 for v in serialized]) + + +def test_serialize_numpy_floating() -> None: + serialized = serialize_attrs( + { + "float16": np.float16(1.0), + "float32": np.float32(1.0), + "float64": np.float64(1.0), + "float128": np.float128(1.0), + } + ) + assert len(serialized) == 4 + assert all([v["value"] == 1.0 for v in serialized]) + + @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() From 5810ef7760a12164f480e10e5f4f2ed2858a458d Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 22 Nov 2023 12:08:35 +0900 Subject: [PATCH 27/99] Cast to str. --- optuna_dashboard/_serializer.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_serializer.py b/optuna_dashboard/_serializer.py index 9b0cce64..2730c328 100644 --- a/optuna_dashboard/_serializer.py +++ b/optuna_dashboard/_serializer.py @@ -105,7 +105,7 @@ def serialize_attrs(attrs: dict[str, Any]) -> list[Attribute]: elif isinstance(v, str): value = v elif isinstance(v, (np.floating, np.integer)): - value = v.item() + value = str(v.item()) else: value = json.dumps(v) value = value[:MAX_ATTR_LENGTH] if len(value) > MAX_ATTR_LENGTH else value From 0aec86dbb1db6fe50bb80df8dea4c633947efca2 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 22 Nov 2023 13:30:02 +0900 Subject: [PATCH 28/99] Fix expected values. --- python_tests/test_serializers.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_tests/test_serializers.py b/python_tests/test_serializers.py index 8c64c5d8..d1bdf59b 100644 --- a/python_tests/test_serializers.py +++ b/python_tests/test_serializers.py @@ -36,7 +36,7 @@ def test_serialize_numpy_integer() -> None: } ) assert len(serialized) == 4 - assert all([v["value"] == 1 for v in serialized]) + assert all([v["value"] == "1" for v in serialized]) def test_serialize_numpy_floating() -> None: @@ -49,7 +49,7 @@ def test_serialize_numpy_floating() -> None: } ) assert len(serialized) == 4 - assert all([v["value"] == 1.0 for v in serialized]) + assert all([v["value"] == "1.0" for v in serialized]) @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") From 7401fae2d66e2e5b73f298293bb34e056582f821 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 22 Nov 2023 13:59:40 +0900 Subject: [PATCH 29/99] Check if value is numbers.Real --- optuna_dashboard/_cached_extra_study_property.py | 10 +++++----- optuna_dashboard/_serializer.py | 6 +++--- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/optuna_dashboard/_cached_extra_study_property.py b/optuna_dashboard/_cached_extra_study_property.py index 1e4fb1a7..919853f4 100644 --- a/optuna_dashboard/_cached_extra_study_property.py +++ b/optuna_dashboard/_cached_extra_study_property.py @@ -1,6 +1,7 @@ from __future__ import annotations import copy +import numbers import threading from typing import Any from typing import List @@ -9,7 +10,6 @@ from typing import Set from typing import Tuple from typing import TYPE_CHECKING -import numpy as np from optuna.distributions import BaseDistribution from optuna.trial import FrozenTrial from optuna.trial import TrialState @@ -86,11 +86,11 @@ class _CachedExtraStudyProperty: self._cursor = next_cursor - def _is_sortable_value(self, v: Any) -> bool: - return not isinstance(v, bool) and isinstance(v, (int, float, np.integer, np.floating)) - def _update_user_attrs(self, trial: FrozenTrial) -> None: - current_user_attrs = {k: self._is_sortable_value(v) for k, v in trial.user_attrs.items()} + current_user_attrs = { + k: not isinstance(v, bool) and isinstance(v, numbers.Real) + for k, v in trial.user_attrs.items() + } for attr_name, current_is_sortable in current_user_attrs.items(): is_sortable = self._union_user_attrs.get(attr_name) if is_sortable is None: diff --git a/optuna_dashboard/_serializer.py b/optuna_dashboard/_serializer.py index 2730c328..dbcdc991 100644 --- a/optuna_dashboard/_serializer.py +++ b/optuna_dashboard/_serializer.py @@ -2,11 +2,11 @@ from __future__ import annotations from datetime import datetime import json +import numbers from typing import Any from typing import TYPE_CHECKING from typing import Union -import numpy as np from optuna.distributions import BaseDistribution from optuna.distributions import CategoricalDistribution from optuna.study import StudySummary @@ -104,8 +104,8 @@ def serialize_attrs(attrs: dict[str, Any]) -> list[Attribute]: value = "" elif isinstance(v, str): value = v - elif isinstance(v, (np.floating, np.integer)): - value = str(v.item()) + elif isinstance(v, numbers.Real): + value = str(v) else: value = json.dumps(v) value = value[:MAX_ATTR_LENGTH] if len(value) > MAX_ATTR_LENGTH else value From f2f5e3665728465d89fafbbd4b34074ecb75490f Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Wed, 22 Nov 2023 06:40:15 +0100 Subject: [PATCH 30/99] Add tests for save_trial_note --- python_tests/test_api.py | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 721bdb56..6c618816 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -9,6 +9,7 @@ from optuna import get_all_study_summaries from optuna.study import StudyDirection from optuna_dashboard._app import create_app from optuna_dashboard._app import create_new_study +from optuna_dashboard._note import note_str_key_prefix, note_ver_key from optuna_dashboard._preference_setting import register_preference_feedback_component from optuna_dashboard._preferential_history import NewHistory from optuna_dashboard._preferential_history import remove_history @@ -261,6 +262,61 @@ class APITestCase(TestCase): self.assertEqual(status, 400) assert study.trials[0].user_attrs == {} + def _test_save_trial_note( + self, request_body: dict[str, int | str] + ) -> tuple[int, optuna.Study]: + study = optuna.create_study() + trial = study.ask() + app = create_app(study._storage) + status, _, _ = send_request( + app, + f"/api/studies/{study._study_id}/{trial._trial_id}/note", + "PUT", + content_type="application/json", + body=json.dumps(request_body), + ) + return status, study + + def test_save_trial_note_overwrite(self) -> None: + study = optuna.create_study() + trial = study.ask() + app = create_app(study._storage) + for ver in range(1, 3): + request_body = {"body": f"Test note ver. {ver}.", "version": ver} + status, _, _ = send_request( + app, + f"/api/studies/{study._study_id}/{trial._trial_id}/note", + "PUT", + content_type="application/json", + body=json.dumps(request_body), + ) + self.assertEqual(status, 204) + # Check if the version 1 is deleted. + assert study.system_attrs == { + note_ver_key(0): request_body["version"], + f"{note_str_key_prefix(0)}{0}": request_body["body"], + } + + def test_save_trial_note(self) -> None: + request_body = {"body": "Test note.", "version": 1} + status, study = self._test_save_trial_note(request_body) + self.assertEqual(status, 204) + assert study.system_attrs == { + note_ver_key(0): request_body["version"], + f"{note_str_key_prefix(0)}{0}": request_body["body"], + } + + def test_save_trial_note_with_wrong_version(self) -> None: + request_body = {"body": "Test note.", "version": 0} + status, study = self._test_save_trial_note(request_body) + self.assertEqual(status, 409) + assert study.system_attrs == {} + + def test_save_trial_note_empty(self) -> None: + status, study = self._test_save_trial_note(request_body={}) + self.assertEqual(status, 400) + assert study.system_attrs == {} + @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_skip_trial(self) -> None: storage = optuna.storages.InMemoryStorage() From f0f876596a1ad1e98d8016a0d03732a1566c3c98 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Wed, 22 Nov 2023 06:47:33 +0100 Subject: [PATCH 31/99] Apply flake8 --- python_tests/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 6c618816..1bae1872 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -294,7 +294,7 @@ class APITestCase(TestCase): # Check if the version 1 is deleted. assert study.system_attrs == { note_ver_key(0): request_body["version"], - f"{note_str_key_prefix(0)}{0}": request_body["body"], + f"{note_str_key_prefix(0)}{0}": request_body["body"], } def test_save_trial_note(self) -> None: @@ -303,7 +303,7 @@ class APITestCase(TestCase): self.assertEqual(status, 204) assert study.system_attrs == { note_ver_key(0): request_body["version"], - f"{note_str_key_prefix(0)}{0}": request_body["body"], + f"{note_str_key_prefix(0)}{0}": request_body["body"], } def test_save_trial_note_with_wrong_version(self) -> None: From dcbd7a3c982694cfc01bcba9c305026675203173 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Wed, 22 Nov 2023 06:52:06 +0100 Subject: [PATCH 32/99] Apply formatter --- python_tests/test_api.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 1bae1872..e62ebba0 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -9,7 +9,8 @@ from optuna import get_all_study_summaries from optuna.study import StudyDirection from optuna_dashboard._app import create_app from optuna_dashboard._app import create_new_study -from optuna_dashboard._note import note_str_key_prefix, note_ver_key +from optuna_dashboard._note import note_str_key_prefix +from optuna_dashboard._note import note_ver_key from optuna_dashboard._preference_setting import register_preference_feedback_component from optuna_dashboard._preferential_history import NewHistory from optuna_dashboard._preferential_history import remove_history From 685e2846135eccfea41fc73b867ca94a961479c5 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Wed, 22 Nov 2023 06:54:55 +0100 Subject: [PATCH 33/99] Apply mypy --- python_tests/test_api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index e62ebba0..0250ad66 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -299,7 +299,7 @@ class APITestCase(TestCase): } def test_save_trial_note(self) -> None: - request_body = {"body": "Test note.", "version": 1} + request_body: dict[str, int | str] = {"body": "Test note.", "version": 1} status, study = self._test_save_trial_note(request_body) self.assertEqual(status, 204) assert study.system_attrs == { @@ -308,7 +308,7 @@ class APITestCase(TestCase): } def test_save_trial_note_with_wrong_version(self) -> None: - request_body = {"body": "Test note.", "version": 0} + request_body: dict[str, int | str] = {"body": "Test note.", "version": 0} status, study = self._test_save_trial_note(request_body) self.assertEqual(status, 409) assert study.system_attrs == {} From ad4d29f70b2f6c2c7e6fbca52f75b9264ac011ae Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 22 Nov 2023 15:22:23 +0900 Subject: [PATCH 34/99] Fix import lines --- optuna_dashboard/_cached_extra_study_property.py | 1 - optuna_dashboard/_serializer.py | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_cached_extra_study_property.py b/optuna_dashboard/_cached_extra_study_property.py index 919853f4..24e22372 100644 --- a/optuna_dashboard/_cached_extra_study_property.py +++ b/optuna_dashboard/_cached_extra_study_property.py @@ -3,7 +3,6 @@ from __future__ import annotations import copy import numbers import threading -from typing import Any from typing import List from typing import Optional from typing import Set diff --git a/optuna_dashboard/_serializer.py b/optuna_dashboard/_serializer.py index dbcdc991..7030abec 100644 --- a/optuna_dashboard/_serializer.py +++ b/optuna_dashboard/_serializer.py @@ -7,6 +7,7 @@ from typing import Any from typing import TYPE_CHECKING from typing import Union +import numpy as np from optuna.distributions import BaseDistribution from optuna.distributions import CategoricalDistribution from optuna.study import StudySummary From d2748eba49e7e946e61d886d3348dc274408b7e7 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Thu, 23 Nov 2023 17:48:37 +0900 Subject: [PATCH 35/99] Fix errors --- optuna_dashboard/ts/components/GraphRank.tsx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index 56693e5d..0ff215d1 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -281,6 +281,9 @@ const plotRank = (rankPlotInfo: RankPlotInfo | null, mode: string) => { template: mode === "dark" ? plotlyDarkTemplate : {}, } + const xValues = rankPlotInfo.xvalues + const yValues = rankPlotInfo.yvalues + const plotData: Partial[] = [ { type: "scatter", From a801b89c5f281a53cc7c850a4457ea671db77019 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Thu, 23 Nov 2023 18:57:12 +0900 Subject: [PATCH 36/99] Add codecov badge --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index badea560..13022125 100644 --- a/README.md +++ b/README.md @@ -3,6 +3,7 @@ ![Software License](https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square) [![PyPI - Downloads](https://img.shields.io/pypi/dm/optuna-dashboard)](https://pypistats.org/packages/optuna-dashboard) [![Read the Docs](https://readthedocs.org/projects/optuna-dashboard/badge/?version=latest)](https://optuna-dashboard.readthedocs.io/en/latest/?badge=latest) +[![Codecov](https://codecov.io/gh/optuna/optuna-dashboard/branch/main/graph/badge.svg)](https://codecov.io/gh/optuna/optuna-dashboard) Real-time dashboard for [Optuna](https://github.com/optuna/optuna). From bf7c930aed187ecc43f6776f4ac9623a7bee3ba9 Mon Sep 17 00:00:00 2001 From: hrntsm Date: Sun, 26 Nov 2023 17:43:05 +0900 Subject: [PATCH 37/99] Add dtype to randint --- optuna_dashboard/preferential/samplers/gp.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/preferential/samplers/gp.py b/optuna_dashboard/preferential/samplers/gp.py index 3cd98061..590c5570 100644 --- a/optuna_dashboard/preferential/samplers/gp.py +++ b/optuna_dashboard/preferential/samplers/gp.py @@ -317,7 +317,7 @@ class PreferentialGPSampler(optuna.samplers.BaseSampler): self._rng = np.random.RandomState(seed) self.independent_sampler = independent_sampler or optuna.samplers.RandomSampler( - seed=self._rng.randint(2**32) + seed=self._rng.randint(2**32, dtype=np.int64) ) self._search_space = optuna.search_space.IntersectionSearchSpace() @@ -355,7 +355,7 @@ class PreferentialGPSampler(optuna.samplers.BaseSampler): ) pref_ids = torch.tensor([[ids[b], ids[w]] for b, w in preferences], dtype=torch.int32) with torch.random.fork_rng(): - torch.manual_seed(self._rng.randint(2**32)) + torch.manual_seed(self._rng.randint(2**32, dtype=np.int64)) self._gp = self._gp or _PreferentialGP( kernel=self.kernel From ba23617127b12fe228144aa86666c2bb723a7a37 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Mon, 27 Nov 2023 14:57:01 +0900 Subject: [PATCH 38/99] Update test_backend.py --- python_tests/artifact/test_backend.py | 54 +++++++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 22544e6f..bdd6c00d 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -1,9 +1,17 @@ +import tempfile +from unittest import TestCase from unittest.mock import MagicMock +import optuna +from optuna.artifacts import FileSystemArtifactStore +from optuna.artifacts import upload_artifact from optuna.storages import BaseStorage +from optuna_dashboard._app import create_app from optuna_dashboard.artifact import _backend import pytest +from ..wsgi_client import send_request + def test_get_artifact_path() -> None: study = MagicMock(_study_id=0) @@ -80,3 +88,49 @@ def test_list_trial_artifacts(init_storage_with_artifact_meta: MagicMock) -> Non {"artifact_id": "id1", "filename": "bar.txt"}, {"artifact_id": "id2", "filename": "baz.txt"}, ] + + +class TestProxyStudyArtifact(TestCase): + def setUp(self) -> None: + self.storage = optuna.storages.InMemoryStorage() + self.study = optuna.create_study(storage=self.storage) + + def test_artifact_store_none(self) -> None: + app = create_app(self.storage) + status, _, body = send_request( + app, + "/artifacts/0/0", + "GET", + content_type="application/json", + ) + self.assertEqual(status, 400) + self.assertEqual(body, b"Cannot access to the artifacts.") + + def test_artifact_not_found(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + app = create_app(self.storage, artifact_store) + status, _, body = send_request( + app, + f"/artifacts/{self.study._study_id}/abc123", + "GET", + content_type="application/json", + ) + self.assertEqual(status, 404) + self.assertEqual(body, b"Not Found") + + def test_successful_artifact_retrieval(self) -> None: + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + with tempfile.NamedTemporaryFile() as f: + f.write(b"dummy_content") + f.flush() + artifact_id = upload_artifact(self.study, f.name, artifact_store=artifact_store) + app = create_app(self.storage, artifact_store) + status, _, _ = send_request( + app, + f"/artifacts/{self.study._study_id}/{artifact_id}", + "GET", + content_type="application/json", + ) + self.assertEqual(status, 200) From f29a6e9c0c57ed6c52e113dbc04d6f3aac3efff5 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Mon, 27 Nov 2023 07:30:37 +0100 Subject: [PATCH 39/99] Address the comments by c-bata --- python_tests/test_api.py | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 0250ad66..601362a1 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -302,21 +302,24 @@ class APITestCase(TestCase): request_body: dict[str, int | str] = {"body": "Test note.", "version": 1} status, study = self._test_save_trial_note(request_body) self.assertEqual(status, 204) - assert study.system_attrs == { + expected_system_attrs = { note_ver_key(0): request_body["version"], f"{note_str_key_prefix(0)}{0}": request_body["body"], } + for k, v in expected_system_attrs.items(): + assert k in study.system_attrs + assert study.system_attrs[k] == v def test_save_trial_note_with_wrong_version(self) -> None: request_body: dict[str, int | str] = {"body": "Test note.", "version": 0} status, study = self._test_save_trial_note(request_body) self.assertEqual(status, 409) - assert study.system_attrs == {} + assert note_ver_key(0) not in study.system_attrs def test_save_trial_note_empty(self) -> None: status, study = self._test_save_trial_note(request_body={}) self.assertEqual(status, 400) - assert study.system_attrs == {} + assert note_ver_key(0) not in study.system_attrs @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") def test_skip_trial(self) -> None: From 6838dd27da9d3fffe0b944952f900b60e832f879 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Mon, 27 Nov 2023 07:31:50 +0100 Subject: [PATCH 40/99] Rename util method in test_save_trial_note --- python_tests/test_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 601362a1..01f4d800 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -263,7 +263,7 @@ class APITestCase(TestCase): self.assertEqual(status, 400) assert study.trials[0].user_attrs == {} - def _test_save_trial_note( + def _save_trial_note( self, request_body: dict[str, int | str] ) -> tuple[int, optuna.Study]: study = optuna.create_study() @@ -300,7 +300,7 @@ class APITestCase(TestCase): def test_save_trial_note(self) -> None: request_body: dict[str, int | str] = {"body": "Test note.", "version": 1} - status, study = self._test_save_trial_note(request_body) + status, study = self._save_trial_note(request_body) self.assertEqual(status, 204) expected_system_attrs = { note_ver_key(0): request_body["version"], @@ -312,12 +312,12 @@ class APITestCase(TestCase): def test_save_trial_note_with_wrong_version(self) -> None: request_body: dict[str, int | str] = {"body": "Test note.", "version": 0} - status, study = self._test_save_trial_note(request_body) + status, study = self._save_trial_note(request_body) self.assertEqual(status, 409) assert note_ver_key(0) not in study.system_attrs def test_save_trial_note_empty(self) -> None: - status, study = self._test_save_trial_note(request_body={}) + status, study = self._save_trial_note(request_body={}) self.assertEqual(status, 400) assert note_ver_key(0) not in study.system_attrs From ca98208cca3caebbf52b04c04fefa60efa2520eb Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Mon, 27 Nov 2023 07:33:38 +0100 Subject: [PATCH 41/99] Replace assertEqual with == because we use pytest as a runner --- python_tests/test_api.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 01f4d800..3c5981ea 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -291,7 +291,7 @@ class APITestCase(TestCase): content_type="application/json", body=json.dumps(request_body), ) - self.assertEqual(status, 204) + assert status == 204 # Check if the version 1 is deleted. assert study.system_attrs == { note_ver_key(0): request_body["version"], @@ -301,7 +301,7 @@ class APITestCase(TestCase): def test_save_trial_note(self) -> None: request_body: dict[str, int | str] = {"body": "Test note.", "version": 1} status, study = self._save_trial_note(request_body) - self.assertEqual(status, 204) + assert status == 204 expected_system_attrs = { note_ver_key(0): request_body["version"], f"{note_str_key_prefix(0)}{0}": request_body["body"], @@ -313,12 +313,12 @@ class APITestCase(TestCase): def test_save_trial_note_with_wrong_version(self) -> None: request_body: dict[str, int | str] = {"body": "Test note.", "version": 0} status, study = self._save_trial_note(request_body) - self.assertEqual(status, 409) + assert status == 409 assert note_ver_key(0) not in study.system_attrs def test_save_trial_note_empty(self) -> None: status, study = self._save_trial_note(request_body={}) - self.assertEqual(status, 400) + assert status == 400 assert note_ver_key(0) not in study.system_attrs @pytest.mark.skipif(sys.version_info < (3, 8), reason="BoTorch dropped Python3.7 support") From 616832c98b3127329ecaa29a289b10420910648a Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Mon, 27 Nov 2023 07:35:12 +0100 Subject: [PATCH 42/99] Apply black to test_api --- python_tests/test_api.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 3c5981ea..2647ed1f 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -263,9 +263,7 @@ class APITestCase(TestCase): self.assertEqual(status, 400) assert study.trials[0].user_attrs == {} - def _save_trial_note( - self, request_body: dict[str, int | str] - ) -> tuple[int, optuna.Study]: + def _save_trial_note(self, request_body: dict[str, int | str]) -> tuple[int, optuna.Study]: study = optuna.create_study() trial = study.ask() app = create_app(study._storage) From 09c95197d8c6c9a5c4b25dc41bd4f2e85aa749f7 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Mon, 27 Nov 2023 07:41:37 +0100 Subject: [PATCH 43/99] Make test_save_trial_note_overwrite more robust --- python_tests/test_api.py | 17 ++++++++++++----- 1 file changed, 12 insertions(+), 5 deletions(-) diff --git a/python_tests/test_api.py b/python_tests/test_api.py index 2647ed1f..e7210710 100644 --- a/python_tests/test_api.py +++ b/python_tests/test_api.py @@ -280,21 +280,28 @@ class APITestCase(TestCase): study = optuna.create_study() trial = study.ask() app = create_app(study._storage) + + def _get_request_body(note_version: int) -> dict[str, str | int]: + return {"body": f"Test note ver. {note_version}.", "version": note_version} + for ver in range(1, 3): - request_body = {"body": f"Test note ver. {ver}.", "version": ver} status, _, _ = send_request( app, f"/api/studies/{study._study_id}/{trial._trial_id}/note", "PUT", content_type="application/json", - body=json.dumps(request_body), + body=json.dumps(_get_request_body(note_version=ver)), ) assert status == 204 # Check if the version 1 is deleted. - assert study.system_attrs == { - note_ver_key(0): request_body["version"], - f"{note_str_key_prefix(0)}{0}": request_body["body"], + expected_request_body = _get_request_body(note_version=2) + expected_system_attrs = { + note_ver_key(trial_id=0): expected_request_body["version"], + f"{note_str_key_prefix(trial_id=0)}{0}": expected_request_body["body"], } + for k, v in expected_system_attrs.items(): + assert k in study.system_attrs + assert study.system_attrs[k] == v def test_save_trial_note(self) -> None: request_body: dict[str, int | str] = {"body": "Test note.", "version": 1} From 4b439a69970c770809ef0e320ac5ea79f3c99685 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Mon, 27 Nov 2023 16:29:00 +0900 Subject: [PATCH 44/99] Update python_tests/artifact/test_backend.py Co-authored-by: c-bata --- python_tests/artifact/test_backend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index bdd6c00d..d7df189a 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -104,7 +104,6 @@ class TestProxyStudyArtifact(TestCase): content_type="application/json", ) self.assertEqual(status, 400) - self.assertEqual(body, b"Cannot access to the artifacts.") def test_artifact_not_found(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: From c89aac71d63f00aca798e25eb605b7e834ec5fc6 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Mon, 27 Nov 2023 16:29:08 +0900 Subject: [PATCH 45/99] Update python_tests/artifact/test_backend.py Co-authored-by: c-bata --- python_tests/artifact/test_backend.py | 1 - 1 file changed, 1 deletion(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index d7df189a..07ea34d7 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -116,7 +116,6 @@ class TestProxyStudyArtifact(TestCase): content_type="application/json", ) self.assertEqual(status, 404) - self.assertEqual(body, b"Not Found") def test_successful_artifact_retrieval(self) -> None: with tempfile.TemporaryDirectory() as tmpdir: From e3cad622f7dc7a96dc983cfef6f03b480a999e41 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Mon, 27 Nov 2023 16:29:17 +0900 Subject: [PATCH 46/99] Update python_tests/artifact/test_backend.py Co-authored-by: c-bata --- python_tests/artifact/test_backend.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 07ea34d7..143e86b4 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -125,10 +125,11 @@ class TestProxyStudyArtifact(TestCase): f.flush() artifact_id = upload_artifact(self.study, f.name, artifact_store=artifact_store) app = create_app(self.storage, artifact_store) - status, _, _ = send_request( + status, _, body = send_request( app, f"/artifacts/{self.study._study_id}/{artifact_id}", "GET", content_type="application/json", ) self.assertEqual(status, 200) + self.assertEqual(body, b"dummy_content") From ff210f50d64ab864bca0cf31f6fad854cd1be1f0 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Mon, 27 Nov 2023 16:30:28 +0900 Subject: [PATCH 47/99] Update test_backend.py --- python_tests/artifact/test_backend.py | 73 ++++++++++++++------------- 1 file changed, 37 insertions(+), 36 deletions(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 143e86b4..00e55d51 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -1,5 +1,4 @@ import tempfile -from unittest import TestCase from unittest.mock import MagicMock import optuna @@ -90,46 +89,48 @@ def test_list_trial_artifacts(init_storage_with_artifact_meta: MagicMock) -> Non ] -class TestProxyStudyArtifact(TestCase): - def setUp(self) -> None: - self.storage = optuna.storages.InMemoryStorage() - self.study = optuna.create_study(storage=self.storage) +def test_artifact_store_none() -> None: + storage = optuna.storages.InMemoryStorage() + app = create_app(storage) + status, _, body = send_request( + app, + "/artifacts/0/0", + "GET", + content_type="application/json", + ) + assert status == 400 - def test_artifact_store_none(self) -> None: - app = create_app(self.storage) + +def test_artifact_not_found() -> None: + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + app = create_app(storage, artifact_store) status, _, body = send_request( app, - "/artifacts/0/0", + f"/artifacts/{study._study_id}/abc123", "GET", content_type="application/json", ) - self.assertEqual(status, 400) + assert status == 404 - def test_artifact_not_found(self) -> None: - with tempfile.TemporaryDirectory() as tmpdir: - artifact_store = FileSystemArtifactStore(tmpdir) - app = create_app(self.storage, artifact_store) - status, _, body = send_request( - app, - f"/artifacts/{self.study._study_id}/abc123", - "GET", - content_type="application/json", - ) - self.assertEqual(status, 404) - def test_successful_artifact_retrieval(self) -> None: - with tempfile.TemporaryDirectory() as tmpdir: - artifact_store = FileSystemArtifactStore(tmpdir) - with tempfile.NamedTemporaryFile() as f: - f.write(b"dummy_content") - f.flush() - artifact_id = upload_artifact(self.study, f.name, artifact_store=artifact_store) - app = create_app(self.storage, artifact_store) - status, _, body = send_request( - app, - f"/artifacts/{self.study._study_id}/{artifact_id}", - "GET", - content_type="application/json", - ) - self.assertEqual(status, 200) - self.assertEqual(body, b"dummy_content") +def test_successful_artifact_retrieval() -> None: + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + with tempfile.NamedTemporaryFile() as f: + f.write(b"dummy_content") + f.flush() + artifact_id = upload_artifact(study, f.name, artifact_store=artifact_store) + app = create_app(storage, artifact_store) + status, _, body = send_request( + app, + f"/artifacts/{study._study_id}/{artifact_id}", + "GET", + content_type="application/json", + ) + assert status == 200 + assert body == b"dummy_content" From 6a55acb662bce6c0e3d43d78f5bf341d91bf1c7d Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 28 Nov 2023 14:20:51 +0900 Subject: [PATCH 48/99] Apply suggestions from code review Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/ts/components/DataGrid.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index faa7e75c..ab5b22e4 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -99,11 +99,11 @@ function DataGrid(props: { return true } const toCellValue = columns[f.columnIdx].toCellValue - const value = + const cellValue = toCellValue !== undefined ? toCellValue(rowIdx) : row[columns[f.columnIdx].field] - return f.values.some((v) => v === value) + return f.values.some((v) => v === cellValue) }) }) @@ -266,17 +266,17 @@ function DataGridHeaderColumn(props: { { - const values = + const newTickedValues = filter === null - ? filterChoices.filter((v) => v !== choice) + ? filterChoices.filter((v) => v !== choice) // By default, every choice is ticked, so the chosen option will be unticked. : filter.values.some((v) => v === choice) ? filter.values.filter((v) => v !== choice) : [...filter.values, choice] - onFilterChange(values) + onFilterChange(newTickedValues) }} > - {!filter || !filter.values.every((v) => v !== choice) ? ( + {!filter || filter.values.some((v) => v === choice) ? ( ) : ( From 18ed414c9af62c7c535a957b34006c7d5950beae Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 28 Nov 2023 14:21:43 +0900 Subject: [PATCH 49/99] Fix lint errors --- optuna_dashboard/ts/components/DataGrid.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/ts/components/DataGrid.tsx b/optuna_dashboard/ts/components/DataGrid.tsx index ab5b22e4..4c24426f 100644 --- a/optuna_dashboard/ts/components/DataGrid.tsx +++ b/optuna_dashboard/ts/components/DataGrid.tsx @@ -268,7 +268,7 @@ function DataGridHeaderColumn(props: { onClick={() => { const newTickedValues = filter === null - ? filterChoices.filter((v) => v !== choice) // By default, every choice is ticked, so the chosen option will be unticked. + ? filterChoices.filter((v) => v !== choice) // By default, every choice is ticked, so the chosen option will be unticked. : filter.values.some((v) => v === choice) ? filter.values.filter((v) => v !== choice) : [...filter.values, choice] From 265dfb4c95afe04f4d30c9df0044c584aa48a158 Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 28 Nov 2023 14:57:01 +0900 Subject: [PATCH 50/99] Update trial artifacts after uploaded --- optuna_dashboard/artifact/_backend.py | 1 + 1 file changed, 1 insertion(+) diff --git a/optuna_dashboard/artifact/_backend.py b/optuna_dashboard/artifact/_backend.py index 9720124b..81cd766d 100644 --- a/optuna_dashboard/artifact/_backend.py +++ b/optuna_dashboard/artifact/_backend.py @@ -139,6 +139,7 @@ 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. return { "artifact_id": artifact_id, "artifacts": list_trial_artifacts(storage.get_study_system_attrs(study_id), trial), From d31ab9a68ddcec2e086538d722a73d334cc930b6 Mon Sep 17 00:00:00 2001 From: Naoto Mizuno Date: Tue, 28 Nov 2023 15:32:21 +0900 Subject: [PATCH 51/99] Add tests for proxy_trial_artifact --- python_tests/artifact/test_backend.py | 55 ++++++++++++++++++++++++--- 1 file changed, 49 insertions(+), 6 deletions(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 00e55d51..14a470e3 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -89,19 +89,18 @@ def test_list_trial_artifacts(init_storage_with_artifact_meta: MagicMock) -> Non ] -def test_artifact_store_none() -> None: +def test_study_artifact_store_none() -> None: storage = optuna.storages.InMemoryStorage() app = create_app(storage) status, _, body = send_request( app, "/artifacts/0/0", "GET", - content_type="application/json", ) assert status == 400 -def test_artifact_not_found() -> None: +def test_study_artifact_not_found() -> None: storage = optuna.storages.InMemoryStorage() study = optuna.create_study(storage=storage) with tempfile.TemporaryDirectory() as tmpdir: @@ -111,12 +110,11 @@ def test_artifact_not_found() -> None: app, f"/artifacts/{study._study_id}/abc123", "GET", - content_type="application/json", ) assert status == 404 -def test_successful_artifact_retrieval() -> None: +def test_successful_study_artifact_retrieval() -> None: storage = optuna.storages.InMemoryStorage() study = optuna.create_study(storage=storage) with tempfile.TemporaryDirectory() as tmpdir: @@ -130,7 +128,52 @@ def test_successful_artifact_retrieval() -> None: app, f"/artifacts/{study._study_id}/{artifact_id}", "GET", - content_type="application/json", + ) + assert status == 200 + assert body == b"dummy_content" + + +def test_trial_artifact_store_none() -> None: + storage = optuna.storages.InMemoryStorage() + app = create_app(storage) + status, _, body = send_request( + app, + "/artifacts/0/0/0", + "GET", + ) + assert status == 400 + + +def test_trial_artifact_not_found() -> None: + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + trial = study.ask() + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + app = create_app(storage, artifact_store) + status, _, body = send_request( + app, + f"/artifacts/{study._study_id}/{trial._trial_id}/abc123", + "GET", + ) + assert status == 404 + + +def test_successful_trial_artifact_retrieval() -> None: + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + trial = study.ask() + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + with tempfile.NamedTemporaryFile() as f: + f.write(b"dummy_content") + f.flush() + artifact_id = upload_artifact(trial, f.name, artifact_store=artifact_store) + app = create_app(storage, artifact_store) + status, _, body = send_request( + app, + f"/artifacts/{study._study_id}/{trial._trial_id}/{artifact_id}", + "GET", ) assert status == 200 assert body == b"dummy_content" From d52939bd8acef13fc4eb4ae5f26d73aa5cdf0792 Mon Sep 17 00:00:00 2001 From: Victoria A <52001888+adjeiv@users.noreply.github.com> Date: Tue, 28 Nov 2023 13:57:15 +0000 Subject: [PATCH 52/99] Transfer to copy, and remove delete API fns --- .ipynb_checkpoints/MD Bug-checkpoint.ipynb | 104 +++++++++++++++++++ .ipynb_checkpoints/Untitled-checkpoint.ipynb | 6 ++ MD Bug.ipynb | 87 ++++++++++++++++ Untitled.ipynb | 85 +++++++++++++++ optuna_dashboard/_app.py | 3 +- optuna_dashboard/_note.py | 49 +++------ python_tests/test_note.py | 52 +--------- 7 files changed, 297 insertions(+), 89 deletions(-) create mode 100644 .ipynb_checkpoints/MD Bug-checkpoint.ipynb create mode 100644 .ipynb_checkpoints/Untitled-checkpoint.ipynb create mode 100644 MD Bug.ipynb create mode 100644 Untitled.ipynb diff --git a/.ipynb_checkpoints/MD Bug-checkpoint.ipynb b/.ipynb_checkpoints/MD Bug-checkpoint.ipynb new file mode 100644 index 00000000..980a307a --- /dev/null +++ b/.ipynb_checkpoints/MD Bug-checkpoint.ipynb @@ -0,0 +1,104 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 5, + "id": "32593824-793e-486e-8494-7f18f73ae5ee", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[I 2023-11-18 12:06:31,357] A new study created in RDB with name: mdbug2\n", + "[W 2023-11-18 12:06:31,454] Trial 0 failed with parameters: {} because of the following error: TypeError(\"tpe_objective_fn() missing 1 required positional argument: 'observations'\").\n", + "Traceback (most recent call last):\n", + " File \"C:\\Users\\victo\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py\", line 200, in _run_trial\n", + " value_or_values = func(trial)\n", + " ^^^^^^^^^^^\n", + "TypeError: tpe_objective_fn() missing 1 required positional argument: 'observations'\n", + "[W 2023-11-18 12:06:31,457] Trial 0 failed with value None.\n" + ] + }, + { + "ename": "TypeError", + "evalue": "tpe_objective_fn() missing 1 required positional argument: 'observations'", + "output_type": "error", + "traceback": [ + "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", + "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", + "Cell \u001b[1;32mIn[5], line 30\u001b[0m\n\u001b[0;32m 25\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m 26\u001b[0m study \u001b[38;5;241m=\u001b[39m optuna\u001b[38;5;241m.\u001b[39mcreate_study(\n\u001b[0;32m 27\u001b[0m storage\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msqlite:///db.sqlite3\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;66;03m# Specify the storage URL here.\u001b[39;00m\n\u001b[0;32m 28\u001b[0m study_name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmdbug2\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 29\u001b[0m )\n\u001b[1;32m---> 30\u001b[0m \u001b[43mstudy\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptimize\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtpe_objective_fn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_trials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m7\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 31\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBest value: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mstudy\u001b[38;5;241m.\u001b[39mbest_value\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m (params: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mstudy\u001b[38;5;241m.\u001b[39mbest_params\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", + "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\study.py:451\u001b[0m, in \u001b[0;36mStudy.optimize\u001b[1;34m(self, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)\u001b[0m\n\u001b[0;32m 348\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21moptimize\u001b[39m(\n\u001b[0;32m 349\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 350\u001b[0m func: ObjectiveFuncType,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 357\u001b[0m show_progress_bar: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m,\n\u001b[0;32m 358\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 359\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Optimize an objective function.\u001b[39;00m\n\u001b[0;32m 360\u001b[0m \n\u001b[0;32m 361\u001b[0m \u001b[38;5;124;03m Optimization is done by choosing a suitable set of hyperparameter values from a given\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 449\u001b[0m \u001b[38;5;124;03m If nested invocation of this method occurs.\u001b[39;00m\n\u001b[0;32m 450\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 451\u001b[0m \u001b[43m_optimize\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 452\u001b[0m \u001b[43m \u001b[49m\u001b[43mstudy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 453\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 454\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_trials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_trials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 455\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 456\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_jobs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_jobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 457\u001b[0m \u001b[43m \u001b[49m\u001b[43mcatch\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mtuple\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43misinstance\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mIterable\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 458\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcallbacks\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 459\u001b[0m \u001b[43m \u001b[49m\u001b[43mgc_after_trial\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgc_after_trial\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 460\u001b[0m \u001b[43m \u001b[49m\u001b[43mshow_progress_bar\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshow_progress_bar\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 461\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", + "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:66\u001b[0m, in \u001b[0;36m_optimize\u001b[1;34m(study, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)\u001b[0m\n\u001b[0;32m 64\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 65\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n_jobs \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m---> 66\u001b[0m \u001b[43m_optimize_sequential\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 67\u001b[0m \u001b[43m \u001b[49m\u001b[43mstudy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 68\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 69\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_trials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 70\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 71\u001b[0m \u001b[43m \u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 72\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 73\u001b[0m \u001b[43m \u001b[49m\u001b[43mgc_after_trial\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 74\u001b[0m \u001b[43m \u001b[49m\u001b[43mreseed_sampler_rng\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 75\u001b[0m \u001b[43m \u001b[49m\u001b[43mtime_start\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 76\u001b[0m \u001b[43m \u001b[49m\u001b[43mprogress_bar\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprogress_bar\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 77\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 78\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 79\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n_jobs \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m:\n", + "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:163\u001b[0m, in \u001b[0;36m_optimize_sequential\u001b[1;34m(study, func, n_trials, timeout, catch, callbacks, gc_after_trial, reseed_sampler_rng, time_start, progress_bar)\u001b[0m\n\u001b[0;32m 160\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m 162\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 163\u001b[0m frozen_trial \u001b[38;5;241m=\u001b[39m \u001b[43m_run_trial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstudy\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 164\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 165\u001b[0m \u001b[38;5;66;03m# The following line mitigates memory problems that can be occurred in some\u001b[39;00m\n\u001b[0;32m 166\u001b[0m \u001b[38;5;66;03m# environments (e.g., services that use computing containers such as GitHub Actions).\u001b[39;00m\n\u001b[0;32m 167\u001b[0m \u001b[38;5;66;03m# Please refer to the following PR for further details:\u001b[39;00m\n\u001b[0;32m 168\u001b[0m \u001b[38;5;66;03m# https://github.com/optuna/optuna/pull/325.\u001b[39;00m\n\u001b[0;32m 169\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m gc_after_trial:\n", + "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:251\u001b[0m, in \u001b[0;36m_run_trial\u001b[1;34m(study, func, catch)\u001b[0m\n\u001b[0;32m 244\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mShould not reach.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 246\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[0;32m 247\u001b[0m frozen_trial\u001b[38;5;241m.\u001b[39mstate \u001b[38;5;241m==\u001b[39m TrialState\u001b[38;5;241m.\u001b[39mFAIL\n\u001b[0;32m 248\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m func_err \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 249\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(func_err, catch)\n\u001b[0;32m 250\u001b[0m ):\n\u001b[1;32m--> 251\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m func_err\n\u001b[0;32m 252\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m frozen_trial\n", + "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:200\u001b[0m, in \u001b[0;36m_run_trial\u001b[1;34m(study, func, catch)\u001b[0m\n\u001b[0;32m 198\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m get_heartbeat_thread(trial\u001b[38;5;241m.\u001b[39m_trial_id, study\u001b[38;5;241m.\u001b[39m_storage):\n\u001b[0;32m 199\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 200\u001b[0m value_or_values \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtrial\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 201\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m exceptions\u001b[38;5;241m.\u001b[39mTrialPruned \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m 202\u001b[0m \u001b[38;5;66;03m# TODO(mamu): Handle multi-objective cases.\u001b[39;00m\n\u001b[0;32m 203\u001b[0m state \u001b[38;5;241m=\u001b[39m TrialState\u001b[38;5;241m.\u001b[39mPRUNED\n", + "\u001b[1;31mTypeError\u001b[0m: tpe_objective_fn() missing 1 required positional argument: 'observations'" + ] + } + ], + "source": [ + "import optuna\n", + "import numpy as np\n", + "\n", + "def cost_function(x, y):\n", + " return np.rand(0, 1)\n", + "\n", + "def tpe_objective_fn(trial):\n", + " num_steps = 5\n", + " num_heat_sources = 7\n", + " lb = [0, 1, 2, 3, 4, 5, 6]\n", + " ub = [4, 5, 6, 7, 8, 9, 10]\n", + " chosen_parameters = []\n", + " for step in range(num_steps):\n", + " for idx in range(num_heat_sources):\n", + " chosen_parameters.append(\n", + " trial.suggest_float(f\"tech_param_{step}_{idx}\", lb[idx], ub[idx])\n", + " )\n", + "\n", + " cost = cost_function(\n", + " chosen_parameters,\n", + " num_steps,\n", + " )\n", + " return cost\n", + "\n", + "if __name__ == \"__main__\":\n", + " study = optuna.create_study(\n", + " storage=\"sqlite:///db.sqlite3\", # Specify the storage URL here.\n", + " study_name=\"mdbug2\"\n", + " )\n", + " study.optimize(tpe_objective_fn, n_trials=7)\n", + " print(f\"Best value: {study.best_value} (params: {study.best_params})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c66fec2-3e46-46c4-bc55-e784d237a3e5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/.ipynb_checkpoints/Untitled-checkpoint.ipynb new file mode 100644 index 00000000..363fcab7 --- /dev/null +++ b/.ipynb_checkpoints/Untitled-checkpoint.ipynb @@ -0,0 +1,6 @@ +{ + "cells": [], + "metadata": {}, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/MD Bug.ipynb b/MD Bug.ipynb new file mode 100644 index 00000000..75b0ea27 --- /dev/null +++ b/MD Bug.ipynb @@ -0,0 +1,87 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 10, + "id": "32593824-793e-486e-8494-7f18f73ae5ee", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "[I 2023-11-18 12:13:05,829] A new study created in RDB with name: mdbugb\n", + "[I 2023-11-18 12:13:06,187] Trial 0 finished with value: 0.9 and parameters: {'tech_param_0_0': 0.9863311667179313, 'tech_param_0_1': 2.9722067965404126, 'tech_param_0_2': 5.479907499743063, 'tech_param_0_3': 6.193147335867399, 'tech_param_0_4': 6.44329845141624, 'tech_param_0_5': 6.957704292533055, 'tech_param_0_6': 6.028116976338117, 'tech_param_1_0': 0.8528770906850203, 'tech_param_1_1': 2.8845780424110843, 'tech_param_1_2': 2.205086423835636, 'tech_param_1_3': 5.8965466201793735, 'tech_param_1_4': 6.765916728183967, 'tech_param_1_5': 5.216406754175934, 'tech_param_1_6': 9.921684209548362, 'tech_param_2_0': 3.9278254745655903, 'tech_param_2_1': 2.8605537473963465, 'tech_param_2_2': 4.466559550814772, 'tech_param_2_3': 4.838464946220083, 'tech_param_2_4': 4.026691756550308, 'tech_param_2_5': 5.3788556566702805, 'tech_param_2_6': 6.675770800145582, 'tech_param_3_0': 3.031562395355724, 'tech_param_3_1': 3.7349707362119777, 'tech_param_3_2': 5.473994937262473, 'tech_param_3_3': 6.881019240236522, 'tech_param_3_4': 6.120001351513482, 'tech_param_3_5': 6.036086717431755, 'tech_param_3_6': 9.160127979158023, 'tech_param_4_0': 2.758775673013467, 'tech_param_4_1': 1.0680707015794786, 'tech_param_4_2': 3.3292082013802666, 'tech_param_4_3': 6.687649735544305, 'tech_param_4_4': 7.443681016669299, 'tech_param_4_5': 7.162694801047951, 'tech_param_4_6': 9.999288376422493}. Best is trial 0 with value: 0.9.\n", + "[I 2023-11-18 12:13:06,501] Trial 1 finished with value: 0.9 and parameters: {'tech_param_0_0': 1.7140076646332538, 'tech_param_0_1': 3.2316252813476325, 'tech_param_0_2': 4.306343376567941, 'tech_param_0_3': 4.61192706012931, 'tech_param_0_4': 5.988119724240175, 'tech_param_0_5': 6.9327357154708285, 'tech_param_0_6': 6.53997726319049, 'tech_param_1_0': 0.3905783000188636, 'tech_param_1_1': 2.76165385255339, 'tech_param_1_2': 2.6085969410537855, 'tech_param_1_3': 4.749816941187031, 'tech_param_1_4': 4.846918571308436, 'tech_param_1_5': 6.254401319703383, 'tech_param_1_6': 7.8373063069546935, 'tech_param_2_0': 2.91103305557099, 'tech_param_2_1': 4.471679319725785, 'tech_param_2_2': 3.5984067288829946, 'tech_param_2_3': 3.470290937133842, 'tech_param_2_4': 7.799315424111551, 'tech_param_2_5': 7.826822170149293, 'tech_param_2_6': 8.849116163752687, 'tech_param_3_0': 3.5380619781168816, 'tech_param_3_1': 3.653451480021751, 'tech_param_3_2': 3.47434126199421, 'tech_param_3_3': 4.443348897470383, 'tech_param_3_4': 7.045847902681517, 'tech_param_3_5': 6.2594143301415475, 'tech_param_3_6': 7.979774488188764, 'tech_param_4_0': 1.0325150795599196, 'tech_param_4_1': 3.4448235367545186, 'tech_param_4_2': 3.3149959756969167, 'tech_param_4_3': 4.819419990796481, 'tech_param_4_4': 7.240391392837244, 'tech_param_4_5': 6.412867172919929, 'tech_param_4_6': 7.784828203240426}. Best is trial 0 with value: 0.9.\n", + "[I 2023-11-18 12:13:06,830] Trial 2 finished with value: 0.9 and parameters: {'tech_param_0_0': 3.7285153030023213, 'tech_param_0_1': 2.967995773245595, 'tech_param_0_2': 2.1806768067032025, 'tech_param_0_3': 4.166177958423523, 'tech_param_0_4': 7.340303214415031, 'tech_param_0_5': 8.466190260210265, 'tech_param_0_6': 8.838603377394787, 'tech_param_1_0': 1.7630336762562222, 'tech_param_1_1': 1.1619500439780515, 'tech_param_1_2': 3.4038384919507747, 'tech_param_1_3': 6.650347896582192, 'tech_param_1_4': 7.7764728822600055, 'tech_param_1_5': 8.639229218462436, 'tech_param_1_6': 7.113069116576301, 'tech_param_2_0': 3.9931082904645394, 'tech_param_2_1': 3.1473900771322443, 'tech_param_2_2': 4.8958785737518475, 'tech_param_2_3': 4.135490374890335, 'tech_param_2_4': 4.642792333607787, 'tech_param_2_5': 6.804997337959285, 'tech_param_2_6': 7.093905518212265, 'tech_param_3_0': 3.7794491588384793, 'tech_param_3_1': 2.9200030292104078, 'tech_param_3_2': 2.193269350123763, 'tech_param_3_3': 4.4228612589114915, 'tech_param_3_4': 6.768417418097517, 'tech_param_3_5': 7.833147977002255, 'tech_param_3_6': 6.043265945471216, 'tech_param_4_0': 1.239068042928749, 'tech_param_4_1': 3.0223635344959376, 'tech_param_4_2': 4.542741924432429, 'tech_param_4_3': 5.527626147325485, 'tech_param_4_4': 6.41989159985463, 'tech_param_4_5': 5.957849401921937, 'tech_param_4_6': 8.220767432559985}. Best is trial 0 with value: 0.9.\n", + "[I 2023-11-18 12:13:07,175] Trial 3 finished with value: 0.9 and parameters: {'tech_param_0_0': 2.3406080881578437, 'tech_param_0_1': 2.20431580814314, 'tech_param_0_2': 2.315142042718149, 'tech_param_0_3': 3.153313622905333, 'tech_param_0_4': 6.434868333320647, 'tech_param_0_5': 8.010675478290011, 'tech_param_0_6': 9.795451780231673, 'tech_param_1_0': 3.893698909905487, 'tech_param_1_1': 3.448571655312496, 'tech_param_1_2': 2.7450589731117088, 'tech_param_1_3': 5.2194903883501, 'tech_param_1_4': 7.236541468886768, 'tech_param_1_5': 6.823921860165192, 'tech_param_1_6': 9.616564141906174, 'tech_param_2_0': 0.39123634971167975, 'tech_param_2_1': 2.39626451267548, 'tech_param_2_2': 3.0034883416963494, 'tech_param_2_3': 6.376136926494553, 'tech_param_2_4': 7.218911507033649, 'tech_param_2_5': 6.750947297023547, 'tech_param_2_6': 8.736332931103936, 'tech_param_3_0': 0.6196213625340548, 'tech_param_3_1': 3.387324252791688, 'tech_param_3_2': 4.183149086442488, 'tech_param_3_3': 5.183411467900889, 'tech_param_3_4': 6.267654527841776, 'tech_param_3_5': 5.549621056761763, 'tech_param_3_6': 7.672308955901496, 'tech_param_4_0': 1.246623944346387, 'tech_param_4_1': 4.456831294515673, 'tech_param_4_2': 2.0724147884920074, 'tech_param_4_3': 5.379419267307556, 'tech_param_4_4': 7.112725631815934, 'tech_param_4_5': 8.87353147211932, 'tech_param_4_6': 7.693639964574908}. Best is trial 0 with value: 0.9.\n", + "[I 2023-11-18 12:13:07,487] Trial 4 finished with value: 0.9 and parameters: {'tech_param_0_0': 3.1068581046485, 'tech_param_0_1': 2.9287122006776443, 'tech_param_0_2': 5.4057937949623085, 'tech_param_0_3': 3.157484488408773, 'tech_param_0_4': 4.690948899704123, 'tech_param_0_5': 8.145312132270456, 'tech_param_0_6': 6.089344894219481, 'tech_param_1_0': 2.9926885191436527, 'tech_param_1_1': 3.176774691423508, 'tech_param_1_2': 2.7721710618333737, 'tech_param_1_3': 4.67762193566592, 'tech_param_1_4': 4.012363168044638, 'tech_param_1_5': 5.659401760473942, 'tech_param_1_6': 8.830934616907934, 'tech_param_2_0': 1.661839241732621, 'tech_param_2_1': 2.7405670060757923, 'tech_param_2_2': 2.100697848478035, 'tech_param_2_3': 6.253340195464311, 'tech_param_2_4': 6.976724037705612, 'tech_param_2_5': 5.510598362317128, 'tech_param_2_6': 7.522477241180887, 'tech_param_3_0': 2.4453135979613685, 'tech_param_3_1': 1.695095408763359, 'tech_param_3_2': 3.102101960127642, 'tech_param_3_3': 4.087680321929806, 'tech_param_3_4': 6.130088543099206, 'tech_param_3_5': 7.350026673357341, 'tech_param_3_6': 8.078588257404482, 'tech_param_4_0': 3.4813120281559478, 'tech_param_4_1': 3.6566237591430295, 'tech_param_4_2': 4.615726645672355, 'tech_param_4_3': 3.5688464780387226, 'tech_param_4_4': 4.212091370332146, 'tech_param_4_5': 6.790648339845854, 'tech_param_4_6': 9.793540661498884}. Best is trial 0 with value: 0.9.\n", + "[I 2023-11-18 12:13:07,804] Trial 5 finished with value: 0.9 and parameters: {'tech_param_0_0': 3.7955449152390734, 'tech_param_0_1': 3.5879051604064425, 'tech_param_0_2': 5.047936105941167, 'tech_param_0_3': 6.409387696471882, 'tech_param_0_4': 6.139796193716612, 'tech_param_0_5': 5.818035000269156, 'tech_param_0_6': 6.77671788266593, 'tech_param_1_0': 3.538002063564025, 'tech_param_1_1': 4.653916400756065, 'tech_param_1_2': 5.139067082232518, 'tech_param_1_3': 5.258612796259207, 'tech_param_1_4': 5.762995511794475, 'tech_param_1_5': 5.914412902115752, 'tech_param_1_6': 8.821608430328544, 'tech_param_2_0': 2.8391352600362043, 'tech_param_2_1': 1.3147834382265242, 'tech_param_2_2': 5.712530324883787, 'tech_param_2_3': 3.9982127746896365, 'tech_param_2_4': 7.59784603286235, 'tech_param_2_5': 8.249304067800995, 'tech_param_2_6': 9.13797124898645, 'tech_param_3_0': 3.4218288637879235, 'tech_param_3_1': 2.8432006830690506, 'tech_param_3_2': 3.705582741959596, 'tech_param_3_3': 4.932782214722957, 'tech_param_3_4': 5.397522930636624, 'tech_param_3_5': 7.665389302007817, 'tech_param_3_6': 8.161440460609116, 'tech_param_4_0': 0.9002808033064547, 'tech_param_4_1': 2.8214804227095938, 'tech_param_4_2': 4.209001287953269, 'tech_param_4_3': 3.4276241669947267, 'tech_param_4_4': 5.65920280643175, 'tech_param_4_5': 7.0296445059400385, 'tech_param_4_6': 6.58394027927913}. Best is trial 0 with value: 0.9.\n", + "[I 2023-11-18 12:13:08,436] Trial 6 finished with value: 0.9 and parameters: {'tech_param_0_0': 2.1661335557317467, 'tech_param_0_1': 2.10482781209877, 'tech_param_0_2': 3.5267055048813605, 'tech_param_0_3': 3.3403923781628193, 'tech_param_0_4': 7.251793183308415, 'tech_param_0_5': 5.419077835893116, 'tech_param_0_6': 6.92337803745288, 'tech_param_1_0': 2.248588320589992, 'tech_param_1_1': 1.5496877038644339, 'tech_param_1_2': 3.034290526391054, 'tech_param_1_3': 5.70430130433426, 'tech_param_1_4': 4.755971043039262, 'tech_param_1_5': 6.032107463009746, 'tech_param_1_6': 6.485937845252712, 'tech_param_2_0': 1.1198667631159256, 'tech_param_2_1': 4.443832638453198, 'tech_param_2_2': 4.029162583096888, 'tech_param_2_3': 4.421249989146505, 'tech_param_2_4': 7.266267320462586, 'tech_param_2_5': 7.272268040989632, 'tech_param_2_6': 9.34608451054512, 'tech_param_3_0': 3.5400627448638406, 'tech_param_3_1': 1.0291771457580832, 'tech_param_3_2': 3.001266350018537, 'tech_param_3_3': 3.83986701735892, 'tech_param_3_4': 4.21380354332598, 'tech_param_3_5': 5.7510339129863555, 'tech_param_3_6': 9.666174111722901, 'tech_param_4_0': 0.25936572770098243, 'tech_param_4_1': 1.0411384740076457, 'tech_param_4_2': 3.6068625395816722, 'tech_param_4_3': 4.778931228821068, 'tech_param_4_4': 5.259093795072825, 'tech_param_4_5': 8.171709511787906, 'tech_param_4_6': 9.580347111957641}. Best is trial 0 with value: 0.9.\n" + ] + } + ], + "source": [ + "import optuna\n", + "import numpy as np\n", + "\n", + "def cost_function(x, y):\n", + " return 0.9\n", + "\n", + "def tpe_objective_fn(trial):\n", + " num_steps = 5\n", + " num_heat_sources = 7\n", + " lb = [0, 1, 2, 3, 4, 5, 6]\n", + " ub = [4, 5, 6, 7, 8, 9, 10]\n", + " chosen_parameters = []\n", + " for step in range(num_steps):\n", + " for idx in range(num_heat_sources):\n", + " chosen_parameters.append(\n", + " trial.suggest_float(f\"tech_param_{step}_{idx}\", lb[idx], ub[idx])\n", + " )\n", + "\n", + " cost = cost_function(\n", + " chosen_parameters,\n", + " num_steps,\n", + " )\n", + " return cost\n", + "\n", + "if __name__ == \"__main__\":\n", + " study = optuna.create_study(\n", + " storage=\"sqlite:///db.sqlite3\", # Specify the storage URL here.\n", + " study_name=\"mdbugb\"\n", + " )\n", + " study.optimize(tpe_objective_fn, n_trials=7)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "2c66fec2-3e46-46c4-bc55-e784d237a3e5", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/Untitled.ipynb b/Untitled.ipynb new file mode 100644 index 00000000..2307d508 --- /dev/null +++ b/Untitled.ipynb @@ -0,0 +1,85 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "c45a38b9-08a7-4ad1-85eb-722b50240f2d", + "metadata": {}, + "outputs": [ + { + "name": "stderr", + "output_type": "stream", + "text": [ + "C:\\Users\\victo\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", + " from .autonotebook import tqdm as notebook_tqdm\n", + "[I 2023-10-21 17:21:34,373] A new study created in RDB with name: quadratic-simple3\n", + "[I 2023-10-21 17:21:34,461] Trial 0 finished with value: 4357.102948257252 and parameters: {'x': 66.0007799064318, 'y': 1}. Best is trial 0 with value: 4357.102948257252.\n", + "[W 2023-10-21 17:21:34,508] Trial 1 failed with parameters: {'x': -21.941587885601493, 'y': -1} because of the following error: The value None could not be cast to float..\n", + "[W 2023-10-21 17:21:34,509] Trial 1 failed with value None.\n", + "[I 2023-10-21 17:21:34,566] Trial 2 finished with value: 2536.6789394289817 and parameters: {'x': 50.37538029066363, 'y': -1}. Best is trial 2 with value: 2536.6789394289817.\n", + "[W 2023-10-21 17:21:34,612] Trial 3 failed with parameters: {'x': -96.83015025022239, 'y': 0} because of the following error: The value None could not be cast to float..\n", + "[W 2023-10-21 17:21:34,613] Trial 3 failed with value None.\n", + "[I 2023-10-21 17:21:34,670] Trial 4 finished with value: 3591.471500742954 and parameters: {'x': -59.920543228036195, 'y': 1}. Best is trial 2 with value: 2536.6789394289817.\n", + "[W 2023-10-21 17:21:34,717] Trial 5 failed with parameters: {'x': -65.11319825521136, 'y': 1} because of the following error: The value None could not be cast to float..\n", + "[W 2023-10-21 17:21:34,717] Trial 5 failed with value None.\n", + "[I 2023-10-21 17:21:34,769] Trial 6 finished with value: 9478.234748213168 and parameters: {'x': -97.35622603723486, 'y': 0}. Best is trial 2 with value: 2536.6789394289817.\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Best value: 2536.6789394289817 (params: {'x': 50.37538029066363, 'y': -1})\n" + ] + } + ], + "source": [ + "import optuna\n", + "\n", + "def objective(trial):\n", + " x = trial.suggest_float(\"x\", -100, 100)\n", + " y = trial.suggest_categorical(\"y\", [-1, 0, 1])\n", + " if trial._trial_id % 2:\n", + " return None\n", + " return x**2 + y\n", + "\n", + "if __name__ == \"__main__\":\n", + " study = optuna.create_study(\n", + " storage=\"sqlite:///db.sqlite3\", # Specify the storage URL here.\n", + " study_name=\"quadratic-simple3\"\n", + " )\n", + " study.optimize(objective, n_trials=7)\n", + " print(f\"Best value: {study.best_value} (params: {study.best_params})\")" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "fedfab0b-c074-4d94-b63f-e6ee7df8e9e0", + "metadata": {}, + "outputs": [], + "source": [] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.11.4" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 32e2a431..f10d051f 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -165,7 +165,7 @@ def create_app( response.status = 500 return {"reason": "Failed to load the new study"} - note.transfer_notes(storage, src_study, dst_study) + note.copy_notes(storage, src_study, dst_study) storage.delete_study(src_study._study_id) response.status = 201 return serialize_study_summary(new_study_summary) @@ -177,7 +177,6 @@ def create_app( delete_all_artifacts(artifact_store, storage, study_id) try: - note.delete_study_notes(storage, study_id) storage.delete_study(study_id) except KeyError: response.status = 404 # Not found diff --git a/optuna_dashboard/_note.py b/optuna_dashboard/_note.py index 57135211..507e3d8f 100644 --- a/optuna_dashboard/_note.py +++ b/optuna_dashboard/_note.py @@ -110,21 +110,17 @@ def note_str_key_prefix(trial_id: Optional[int]) -> str: return f"dashboard:{trial_id}:note_str:" -def transfer_notes(storage: BaseStorage, src_study: optuna.Study, dst_study: optuna.Study) -> None: +def copy_notes(storage: BaseStorage, src_study: optuna.Study, dst_study: optuna.Study) -> None: system_attrs = storage.get_study_system_attrs(study_id=src_study._study_id) - def transfer(src_trial_id: Optional[int], dst_trial_id: Optional[int]) -> None: - note = get_note_from_system_attrs(system_attrs, src_trial_id)["body"] - save_note_with_version(storage, dst_study._study_id, dst_trial_id, 0, note) - delete_notes(storage, src_study._study_id, src_trial_id) - - # Transfer individual trial notes + # Copy individual trial notes for src_trial, dst_trial in zip(src_study.get_trials(), dst_study.get_trials()): - transfer(src_trial._trial_id, dst_trial._trial_id) + note = get_note_from_system_attrs(system_attrs, src_trial._trial_id)["body"] + save_note_with_version(storage, dst_study._study_id, dst_trial._trial_id, 0, note) - # Transfer study note - NO_SRC_TRIAL, NO_DST_TRIAL = None, None - transfer(NO_SRC_TRIAL, NO_DST_TRIAL) + # Copy study note + note = get_note_from_system_attrs(system_attrs, None)["body"] + save_note_with_version(storage, dst_study._study_id, None, 0, note) def get_note_from_system_attrs(system_attrs: dict[str, Any], trial_id: Optional[int]) -> NoteType: @@ -149,17 +145,6 @@ def version_is_incremented( return req_note_ver == db_note_ver + 1 -def all_trial_notes( - storage: BaseStorage, study_id: int, trial_id: Optional[int] -) -> dict[str, str]: - all_note_attrs: dict[str, str] = { - key: value - for key, value in storage.get_study_system_attrs(study_id).items() - if key.startswith(note_str_key_prefix(trial_id)) - } - return all_note_attrs - - def save_note_with_version( storage: BaseStorage, study_id: int, trial_id: Optional[int], ver: int, body: str ) -> None: @@ -170,26 +155,16 @@ def save_note_with_version( storage.set_study_system_attr(study_id, k, v) # Clear previous messages - all_note_attrs = all_trial_notes(storage, study_id, trial_id) + all_note_attrs: dict[str, str] = { + key: value + for key, value in storage.get_study_system_attrs(study_id).items() + if key.startswith(note_str_key_prefix(trial_id)) + } if len(all_note_attrs) > len(attrs): for i in range(len(attrs), len(all_note_attrs)): storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") -def delete_study_notes(storage: BaseStorage, study_id: int) -> None: - for trial in storage.get_all_trials(study_id): - delete_notes(storage, study_id, trial._trial_id) - - delete_notes(storage, study_id, None) - - -def delete_notes(storage: BaseStorage, study_id: int, trial_id: Optional[int]) -> None: - all_note_attrs = all_trial_notes(storage, study_id, trial_id) - - for i in range(len(all_note_attrs)): - storage.set_study_system_attr(study_id, f"{note_str_key_prefix(trial_id)}{i}", "") - - def split_body(note_str: str, trial_id: Optional[int]) -> dict[str, str]: note_len = len(note_str) attrs = {} diff --git a/python_tests/test_note.py b/python_tests/test_note.py index 55c8f610..592cf3e7 100644 --- a/python_tests/test_note.py +++ b/python_tests/test_note.py @@ -54,50 +54,7 @@ class NoteTestCase(TestCase): self.assertEqual(note_dict["body"], body) self.assertEqual(note_dict["version"], expected_ver) - def test_delete_notes_trial(self) -> None: - study = optuna.create_study() - trials = [ - study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) for _ in range(2) - ] - storage = study._storage - - notes = ["trial 0", "trial 1"] - for trial, body in zip(trials, notes): - with self.subTest(body): - save_note(trial, body) - - self.assertEqual(get_note(trial), body) - - note.delete_notes(storage, study._study_id, trial._trial_id) - - self.assertEqual(get_note(trial), "") - - def test_delete_notes_study(self) -> None: - study = optuna.create_study() - trials = [ - study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) for _ in range(2) - ] - storage = study._storage - - notes = ["trial 0", "trial 1"] - for trial, body in zip(trials, notes): - with self.subTest(body): - save_note(trial, body) - - actual = get_note(trial) - self.assertEqual(actual, body) - - save_note(study, "Study note") - actual = get_note(study) - self.assertEqual(actual, "Study note") - - note.delete_study_notes(storage, study._study_id) - - for trial in trials: - self.assertEqual(get_note(trial), "") - self.assertEqual(get_note(study), "") - - def test_transfer_notes(self) -> None: + def test_copy_notes(self) -> None: old_study = optuna.create_study() old_trials = [ old_study.ask({"x1": optuna.distributions.FloatDistribution(0, 10)}) for _ in range(2) @@ -112,12 +69,7 @@ class NoteTestCase(TestCase): new_study = optuna.create_study(storage=storage, directions=old_study.directions) new_study.add_trials(old_study.get_trials(deepcopy=False)) - note.transfer_notes(storage, old_study, new_study) - - for old_trial in old_trials: - self.assertEqual(get_note(old_trial), "") - self.assertEqual(get_note(old_study), "") - + note.copy_notes(storage, old_study, new_study) system_attrs = new_study._storage.get_study_system_attrs(new_study._study_id) for new_trial, body in zip(new_study.get_trials(), notes): actual = note.get_note_from_system_attrs(system_attrs, new_trial._trial_id) From 6873b3b9ba576125e6ff737a4cd0af41a469bb33 Mon Sep 17 00:00:00 2001 From: Victoria A <52001888+adjeiv@users.noreply.github.com> Date: Tue, 28 Nov 2023 14:02:44 +0000 Subject: [PATCH 53/99] Remove added files --- .ipynb_checkpoints/MD Bug-checkpoint.ipynb | 104 ------------------- .ipynb_checkpoints/Untitled-checkpoint.ipynb | 6 -- MD Bug.ipynb | 87 ---------------- Untitled.ipynb | 85 --------------- 4 files changed, 282 deletions(-) delete mode 100644 .ipynb_checkpoints/MD Bug-checkpoint.ipynb delete mode 100644 .ipynb_checkpoints/Untitled-checkpoint.ipynb delete mode 100644 MD Bug.ipynb delete mode 100644 Untitled.ipynb diff --git a/.ipynb_checkpoints/MD Bug-checkpoint.ipynb b/.ipynb_checkpoints/MD Bug-checkpoint.ipynb deleted file mode 100644 index 980a307a..00000000 --- a/.ipynb_checkpoints/MD Bug-checkpoint.ipynb +++ /dev/null @@ -1,104 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 5, - "id": "32593824-793e-486e-8494-7f18f73ae5ee", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[I 2023-11-18 12:06:31,357] A new study created in RDB with name: mdbug2\n", - "[W 2023-11-18 12:06:31,454] Trial 0 failed with parameters: {} because of the following error: TypeError(\"tpe_objective_fn() missing 1 required positional argument: 'observations'\").\n", - "Traceback (most recent call last):\n", - " File \"C:\\Users\\victo\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py\", line 200, in _run_trial\n", - " value_or_values = func(trial)\n", - " ^^^^^^^^^^^\n", - "TypeError: tpe_objective_fn() missing 1 required positional argument: 'observations'\n", - "[W 2023-11-18 12:06:31,457] Trial 0 failed with value None.\n" - ] - }, - { - "ename": "TypeError", - "evalue": "tpe_objective_fn() missing 1 required positional argument: 'observations'", - "output_type": "error", - "traceback": [ - "\u001b[1;31m---------------------------------------------------------------------------\u001b[0m", - "\u001b[1;31mTypeError\u001b[0m Traceback (most recent call last)", - "Cell \u001b[1;32mIn[5], line 30\u001b[0m\n\u001b[0;32m 25\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m \u001b[38;5;18m__name__\u001b[39m \u001b[38;5;241m==\u001b[39m \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124m__main__\u001b[39m\u001b[38;5;124m\"\u001b[39m:\n\u001b[0;32m 26\u001b[0m study \u001b[38;5;241m=\u001b[39m optuna\u001b[38;5;241m.\u001b[39mcreate_study(\n\u001b[0;32m 27\u001b[0m storage\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124msqlite:///db.sqlite3\u001b[39m\u001b[38;5;124m\"\u001b[39m, \u001b[38;5;66;03m# Specify the storage URL here.\u001b[39;00m\n\u001b[0;32m 28\u001b[0m study_name\u001b[38;5;241m=\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mmdbug2\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 29\u001b[0m )\n\u001b[1;32m---> 30\u001b[0m \u001b[43mstudy\u001b[49m\u001b[38;5;241;43m.\u001b[39;49m\u001b[43moptimize\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtpe_objective_fn\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mn_trials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;241;43m7\u001b[39;49m\u001b[43m)\u001b[49m\n\u001b[0;32m 31\u001b[0m \u001b[38;5;28mprint\u001b[39m(\u001b[38;5;124mf\u001b[39m\u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mBest value: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mstudy\u001b[38;5;241m.\u001b[39mbest_value\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m (params: \u001b[39m\u001b[38;5;132;01m{\u001b[39;00mstudy\u001b[38;5;241m.\u001b[39mbest_params\u001b[38;5;132;01m}\u001b[39;00m\u001b[38;5;124m)\u001b[39m\u001b[38;5;124m\"\u001b[39m)\n", - "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\study.py:451\u001b[0m, in \u001b[0;36mStudy.optimize\u001b[1;34m(self, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)\u001b[0m\n\u001b[0;32m 348\u001b[0m \u001b[38;5;28;01mdef\u001b[39;00m \u001b[38;5;21moptimize\u001b[39m(\n\u001b[0;32m 349\u001b[0m \u001b[38;5;28mself\u001b[39m,\n\u001b[0;32m 350\u001b[0m func: ObjectiveFuncType,\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 357\u001b[0m show_progress_bar: \u001b[38;5;28mbool\u001b[39m \u001b[38;5;241m=\u001b[39m \u001b[38;5;28;01mFalse\u001b[39;00m,\n\u001b[0;32m 358\u001b[0m ) \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m>\u001b[39m \u001b[38;5;28;01mNone\u001b[39;00m:\n\u001b[0;32m 359\u001b[0m \u001b[38;5;250m \u001b[39m\u001b[38;5;124;03m\"\"\"Optimize an objective function.\u001b[39;00m\n\u001b[0;32m 360\u001b[0m \n\u001b[0;32m 361\u001b[0m \u001b[38;5;124;03m Optimization is done by choosing a suitable set of hyperparameter values from a given\u001b[39;00m\n\u001b[1;32m (...)\u001b[0m\n\u001b[0;32m 449\u001b[0m \u001b[38;5;124;03m If nested invocation of this method occurs.\u001b[39;00m\n\u001b[0;32m 450\u001b[0m \u001b[38;5;124;03m \"\"\"\u001b[39;00m\n\u001b[1;32m--> 451\u001b[0m \u001b[43m_optimize\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 452\u001b[0m \u001b[43m \u001b[49m\u001b[43mstudy\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mself\u001b[39;49m\u001b[43m,\u001b[49m\n\u001b[0;32m 453\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 454\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_trials\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_trials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 455\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 456\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_jobs\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mn_jobs\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 457\u001b[0m \u001b[43m \u001b[49m\u001b[43mcatch\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43mtuple\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01mif\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[38;5;28;43misinstance\u001b[39;49m\u001b[43m(\u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mIterable\u001b[49m\u001b[43m)\u001b[49m\u001b[43m \u001b[49m\u001b[38;5;28;43;01melse\u001b[39;49;00m\u001b[43m \u001b[49m\u001b[43m(\u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m,\u001b[49m\u001b[43m)\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 458\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mcallbacks\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 459\u001b[0m \u001b[43m \u001b[49m\u001b[43mgc_after_trial\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mgc_after_trial\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 460\u001b[0m \u001b[43m \u001b[49m\u001b[43mshow_progress_bar\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mshow_progress_bar\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 461\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n", - "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:66\u001b[0m, in \u001b[0;36m_optimize\u001b[1;34m(study, func, n_trials, timeout, n_jobs, catch, callbacks, gc_after_trial, show_progress_bar)\u001b[0m\n\u001b[0;32m 64\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[0;32m 65\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n_jobs \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m1\u001b[39m:\n\u001b[1;32m---> 66\u001b[0m \u001b[43m_optimize_sequential\u001b[49m\u001b[43m(\u001b[49m\n\u001b[0;32m 67\u001b[0m \u001b[43m \u001b[49m\u001b[43mstudy\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 68\u001b[0m \u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 69\u001b[0m \u001b[43m \u001b[49m\u001b[43mn_trials\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 70\u001b[0m \u001b[43m \u001b[49m\u001b[43mtimeout\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 71\u001b[0m \u001b[43m \u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 72\u001b[0m \u001b[43m \u001b[49m\u001b[43mcallbacks\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 73\u001b[0m \u001b[43m \u001b[49m\u001b[43mgc_after_trial\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 74\u001b[0m \u001b[43m \u001b[49m\u001b[43mreseed_sampler_rng\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mFalse\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 75\u001b[0m \u001b[43m \u001b[49m\u001b[43mtime_start\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[38;5;28;43;01mNone\u001b[39;49;00m\u001b[43m,\u001b[49m\n\u001b[0;32m 76\u001b[0m \u001b[43m \u001b[49m\u001b[43mprogress_bar\u001b[49m\u001b[38;5;241;43m=\u001b[39;49m\u001b[43mprogress_bar\u001b[49m\u001b[43m,\u001b[49m\n\u001b[0;32m 77\u001b[0m \u001b[43m \u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 78\u001b[0m \u001b[38;5;28;01melse\u001b[39;00m:\n\u001b[0;32m 79\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m n_jobs \u001b[38;5;241m==\u001b[39m \u001b[38;5;241m-\u001b[39m\u001b[38;5;241m1\u001b[39m:\n", - "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:163\u001b[0m, in \u001b[0;36m_optimize_sequential\u001b[1;34m(study, func, n_trials, timeout, catch, callbacks, gc_after_trial, reseed_sampler_rng, time_start, progress_bar)\u001b[0m\n\u001b[0;32m 160\u001b[0m \u001b[38;5;28;01mbreak\u001b[39;00m\n\u001b[0;32m 162\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 163\u001b[0m frozen_trial \u001b[38;5;241m=\u001b[39m \u001b[43m_run_trial\u001b[49m\u001b[43m(\u001b[49m\u001b[43mstudy\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mfunc\u001b[49m\u001b[43m,\u001b[49m\u001b[43m \u001b[49m\u001b[43mcatch\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 164\u001b[0m \u001b[38;5;28;01mfinally\u001b[39;00m:\n\u001b[0;32m 165\u001b[0m \u001b[38;5;66;03m# The following line mitigates memory problems that can be occurred in some\u001b[39;00m\n\u001b[0;32m 166\u001b[0m \u001b[38;5;66;03m# environments (e.g., services that use computing containers such as GitHub Actions).\u001b[39;00m\n\u001b[0;32m 167\u001b[0m \u001b[38;5;66;03m# Please refer to the following PR for further details:\u001b[39;00m\n\u001b[0;32m 168\u001b[0m \u001b[38;5;66;03m# https://github.com/optuna/optuna/pull/325.\u001b[39;00m\n\u001b[0;32m 169\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m gc_after_trial:\n", - "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:251\u001b[0m, in \u001b[0;36m_run_trial\u001b[1;34m(study, func, catch)\u001b[0m\n\u001b[0;32m 244\u001b[0m \u001b[38;5;28;01massert\u001b[39;00m \u001b[38;5;28;01mFalse\u001b[39;00m, \u001b[38;5;124m\"\u001b[39m\u001b[38;5;124mShould not reach.\u001b[39m\u001b[38;5;124m\"\u001b[39m\n\u001b[0;32m 246\u001b[0m \u001b[38;5;28;01mif\u001b[39;00m (\n\u001b[0;32m 247\u001b[0m frozen_trial\u001b[38;5;241m.\u001b[39mstate \u001b[38;5;241m==\u001b[39m TrialState\u001b[38;5;241m.\u001b[39mFAIL\n\u001b[0;32m 248\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m func_err \u001b[38;5;129;01mis\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28;01mNone\u001b[39;00m\n\u001b[0;32m 249\u001b[0m \u001b[38;5;129;01mand\u001b[39;00m \u001b[38;5;129;01mnot\u001b[39;00m \u001b[38;5;28misinstance\u001b[39m(func_err, catch)\n\u001b[0;32m 250\u001b[0m ):\n\u001b[1;32m--> 251\u001b[0m \u001b[38;5;28;01mraise\u001b[39;00m func_err\n\u001b[0;32m 252\u001b[0m \u001b[38;5;28;01mreturn\u001b[39;00m frozen_trial\n", - "File \u001b[1;32m~\\Documents\\OpenSource\\optuna_workspace\\optuna\\optuna\\study\\_optimize.py:200\u001b[0m, in \u001b[0;36m_run_trial\u001b[1;34m(study, func, catch)\u001b[0m\n\u001b[0;32m 198\u001b[0m \u001b[38;5;28;01mwith\u001b[39;00m get_heartbeat_thread(trial\u001b[38;5;241m.\u001b[39m_trial_id, study\u001b[38;5;241m.\u001b[39m_storage):\n\u001b[0;32m 199\u001b[0m \u001b[38;5;28;01mtry\u001b[39;00m:\n\u001b[1;32m--> 200\u001b[0m value_or_values \u001b[38;5;241m=\u001b[39m \u001b[43mfunc\u001b[49m\u001b[43m(\u001b[49m\u001b[43mtrial\u001b[49m\u001b[43m)\u001b[49m\n\u001b[0;32m 201\u001b[0m \u001b[38;5;28;01mexcept\u001b[39;00m exceptions\u001b[38;5;241m.\u001b[39mTrialPruned \u001b[38;5;28;01mas\u001b[39;00m e:\n\u001b[0;32m 202\u001b[0m \u001b[38;5;66;03m# TODO(mamu): Handle multi-objective cases.\u001b[39;00m\n\u001b[0;32m 203\u001b[0m state \u001b[38;5;241m=\u001b[39m TrialState\u001b[38;5;241m.\u001b[39mPRUNED\n", - "\u001b[1;31mTypeError\u001b[0m: tpe_objective_fn() missing 1 required positional argument: 'observations'" - ] - } - ], - "source": [ - "import optuna\n", - "import numpy as np\n", - "\n", - "def cost_function(x, y):\n", - " return np.rand(0, 1)\n", - "\n", - "def tpe_objective_fn(trial):\n", - " num_steps = 5\n", - " num_heat_sources = 7\n", - " lb = [0, 1, 2, 3, 4, 5, 6]\n", - " ub = [4, 5, 6, 7, 8, 9, 10]\n", - " chosen_parameters = []\n", - " for step in range(num_steps):\n", - " for idx in range(num_heat_sources):\n", - " chosen_parameters.append(\n", - " trial.suggest_float(f\"tech_param_{step}_{idx}\", lb[idx], ub[idx])\n", - " )\n", - "\n", - " cost = cost_function(\n", - " chosen_parameters,\n", - " num_steps,\n", - " )\n", - " return cost\n", - "\n", - "if __name__ == \"__main__\":\n", - " study = optuna.create_study(\n", - " storage=\"sqlite:///db.sqlite3\", # Specify the storage URL here.\n", - " study_name=\"mdbug2\"\n", - " )\n", - " study.optimize(tpe_objective_fn, n_trials=7)\n", - " print(f\"Best value: {study.best_value} (params: {study.best_params})\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c66fec2-3e46-46c4-bc55-e784d237a3e5", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/.ipynb_checkpoints/Untitled-checkpoint.ipynb b/.ipynb_checkpoints/Untitled-checkpoint.ipynb deleted file mode 100644 index 363fcab7..00000000 --- a/.ipynb_checkpoints/Untitled-checkpoint.ipynb +++ /dev/null @@ -1,6 +0,0 @@ -{ - "cells": [], - "metadata": {}, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/MD Bug.ipynb b/MD Bug.ipynb deleted file mode 100644 index 75b0ea27..00000000 --- a/MD Bug.ipynb +++ /dev/null @@ -1,87 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 10, - "id": "32593824-793e-486e-8494-7f18f73ae5ee", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "[I 2023-11-18 12:13:05,829] A new study created in RDB with name: mdbugb\n", - "[I 2023-11-18 12:13:06,187] Trial 0 finished with value: 0.9 and parameters: {'tech_param_0_0': 0.9863311667179313, 'tech_param_0_1': 2.9722067965404126, 'tech_param_0_2': 5.479907499743063, 'tech_param_0_3': 6.193147335867399, 'tech_param_0_4': 6.44329845141624, 'tech_param_0_5': 6.957704292533055, 'tech_param_0_6': 6.028116976338117, 'tech_param_1_0': 0.8528770906850203, 'tech_param_1_1': 2.8845780424110843, 'tech_param_1_2': 2.205086423835636, 'tech_param_1_3': 5.8965466201793735, 'tech_param_1_4': 6.765916728183967, 'tech_param_1_5': 5.216406754175934, 'tech_param_1_6': 9.921684209548362, 'tech_param_2_0': 3.9278254745655903, 'tech_param_2_1': 2.8605537473963465, 'tech_param_2_2': 4.466559550814772, 'tech_param_2_3': 4.838464946220083, 'tech_param_2_4': 4.026691756550308, 'tech_param_2_5': 5.3788556566702805, 'tech_param_2_6': 6.675770800145582, 'tech_param_3_0': 3.031562395355724, 'tech_param_3_1': 3.7349707362119777, 'tech_param_3_2': 5.473994937262473, 'tech_param_3_3': 6.881019240236522, 'tech_param_3_4': 6.120001351513482, 'tech_param_3_5': 6.036086717431755, 'tech_param_3_6': 9.160127979158023, 'tech_param_4_0': 2.758775673013467, 'tech_param_4_1': 1.0680707015794786, 'tech_param_4_2': 3.3292082013802666, 'tech_param_4_3': 6.687649735544305, 'tech_param_4_4': 7.443681016669299, 'tech_param_4_5': 7.162694801047951, 'tech_param_4_6': 9.999288376422493}. Best is trial 0 with value: 0.9.\n", - "[I 2023-11-18 12:13:06,501] Trial 1 finished with value: 0.9 and parameters: {'tech_param_0_0': 1.7140076646332538, 'tech_param_0_1': 3.2316252813476325, 'tech_param_0_2': 4.306343376567941, 'tech_param_0_3': 4.61192706012931, 'tech_param_0_4': 5.988119724240175, 'tech_param_0_5': 6.9327357154708285, 'tech_param_0_6': 6.53997726319049, 'tech_param_1_0': 0.3905783000188636, 'tech_param_1_1': 2.76165385255339, 'tech_param_1_2': 2.6085969410537855, 'tech_param_1_3': 4.749816941187031, 'tech_param_1_4': 4.846918571308436, 'tech_param_1_5': 6.254401319703383, 'tech_param_1_6': 7.8373063069546935, 'tech_param_2_0': 2.91103305557099, 'tech_param_2_1': 4.471679319725785, 'tech_param_2_2': 3.5984067288829946, 'tech_param_2_3': 3.470290937133842, 'tech_param_2_4': 7.799315424111551, 'tech_param_2_5': 7.826822170149293, 'tech_param_2_6': 8.849116163752687, 'tech_param_3_0': 3.5380619781168816, 'tech_param_3_1': 3.653451480021751, 'tech_param_3_2': 3.47434126199421, 'tech_param_3_3': 4.443348897470383, 'tech_param_3_4': 7.045847902681517, 'tech_param_3_5': 6.2594143301415475, 'tech_param_3_6': 7.979774488188764, 'tech_param_4_0': 1.0325150795599196, 'tech_param_4_1': 3.4448235367545186, 'tech_param_4_2': 3.3149959756969167, 'tech_param_4_3': 4.819419990796481, 'tech_param_4_4': 7.240391392837244, 'tech_param_4_5': 6.412867172919929, 'tech_param_4_6': 7.784828203240426}. Best is trial 0 with value: 0.9.\n", - "[I 2023-11-18 12:13:06,830] Trial 2 finished with value: 0.9 and parameters: {'tech_param_0_0': 3.7285153030023213, 'tech_param_0_1': 2.967995773245595, 'tech_param_0_2': 2.1806768067032025, 'tech_param_0_3': 4.166177958423523, 'tech_param_0_4': 7.340303214415031, 'tech_param_0_5': 8.466190260210265, 'tech_param_0_6': 8.838603377394787, 'tech_param_1_0': 1.7630336762562222, 'tech_param_1_1': 1.1619500439780515, 'tech_param_1_2': 3.4038384919507747, 'tech_param_1_3': 6.650347896582192, 'tech_param_1_4': 7.7764728822600055, 'tech_param_1_5': 8.639229218462436, 'tech_param_1_6': 7.113069116576301, 'tech_param_2_0': 3.9931082904645394, 'tech_param_2_1': 3.1473900771322443, 'tech_param_2_2': 4.8958785737518475, 'tech_param_2_3': 4.135490374890335, 'tech_param_2_4': 4.642792333607787, 'tech_param_2_5': 6.804997337959285, 'tech_param_2_6': 7.093905518212265, 'tech_param_3_0': 3.7794491588384793, 'tech_param_3_1': 2.9200030292104078, 'tech_param_3_2': 2.193269350123763, 'tech_param_3_3': 4.4228612589114915, 'tech_param_3_4': 6.768417418097517, 'tech_param_3_5': 7.833147977002255, 'tech_param_3_6': 6.043265945471216, 'tech_param_4_0': 1.239068042928749, 'tech_param_4_1': 3.0223635344959376, 'tech_param_4_2': 4.542741924432429, 'tech_param_4_3': 5.527626147325485, 'tech_param_4_4': 6.41989159985463, 'tech_param_4_5': 5.957849401921937, 'tech_param_4_6': 8.220767432559985}. Best is trial 0 with value: 0.9.\n", - "[I 2023-11-18 12:13:07,175] Trial 3 finished with value: 0.9 and parameters: {'tech_param_0_0': 2.3406080881578437, 'tech_param_0_1': 2.20431580814314, 'tech_param_0_2': 2.315142042718149, 'tech_param_0_3': 3.153313622905333, 'tech_param_0_4': 6.434868333320647, 'tech_param_0_5': 8.010675478290011, 'tech_param_0_6': 9.795451780231673, 'tech_param_1_0': 3.893698909905487, 'tech_param_1_1': 3.448571655312496, 'tech_param_1_2': 2.7450589731117088, 'tech_param_1_3': 5.2194903883501, 'tech_param_1_4': 7.236541468886768, 'tech_param_1_5': 6.823921860165192, 'tech_param_1_6': 9.616564141906174, 'tech_param_2_0': 0.39123634971167975, 'tech_param_2_1': 2.39626451267548, 'tech_param_2_2': 3.0034883416963494, 'tech_param_2_3': 6.376136926494553, 'tech_param_2_4': 7.218911507033649, 'tech_param_2_5': 6.750947297023547, 'tech_param_2_6': 8.736332931103936, 'tech_param_3_0': 0.6196213625340548, 'tech_param_3_1': 3.387324252791688, 'tech_param_3_2': 4.183149086442488, 'tech_param_3_3': 5.183411467900889, 'tech_param_3_4': 6.267654527841776, 'tech_param_3_5': 5.549621056761763, 'tech_param_3_6': 7.672308955901496, 'tech_param_4_0': 1.246623944346387, 'tech_param_4_1': 4.456831294515673, 'tech_param_4_2': 2.0724147884920074, 'tech_param_4_3': 5.379419267307556, 'tech_param_4_4': 7.112725631815934, 'tech_param_4_5': 8.87353147211932, 'tech_param_4_6': 7.693639964574908}. Best is trial 0 with value: 0.9.\n", - "[I 2023-11-18 12:13:07,487] Trial 4 finished with value: 0.9 and parameters: {'tech_param_0_0': 3.1068581046485, 'tech_param_0_1': 2.9287122006776443, 'tech_param_0_2': 5.4057937949623085, 'tech_param_0_3': 3.157484488408773, 'tech_param_0_4': 4.690948899704123, 'tech_param_0_5': 8.145312132270456, 'tech_param_0_6': 6.089344894219481, 'tech_param_1_0': 2.9926885191436527, 'tech_param_1_1': 3.176774691423508, 'tech_param_1_2': 2.7721710618333737, 'tech_param_1_3': 4.67762193566592, 'tech_param_1_4': 4.012363168044638, 'tech_param_1_5': 5.659401760473942, 'tech_param_1_6': 8.830934616907934, 'tech_param_2_0': 1.661839241732621, 'tech_param_2_1': 2.7405670060757923, 'tech_param_2_2': 2.100697848478035, 'tech_param_2_3': 6.253340195464311, 'tech_param_2_4': 6.976724037705612, 'tech_param_2_5': 5.510598362317128, 'tech_param_2_6': 7.522477241180887, 'tech_param_3_0': 2.4453135979613685, 'tech_param_3_1': 1.695095408763359, 'tech_param_3_2': 3.102101960127642, 'tech_param_3_3': 4.087680321929806, 'tech_param_3_4': 6.130088543099206, 'tech_param_3_5': 7.350026673357341, 'tech_param_3_6': 8.078588257404482, 'tech_param_4_0': 3.4813120281559478, 'tech_param_4_1': 3.6566237591430295, 'tech_param_4_2': 4.615726645672355, 'tech_param_4_3': 3.5688464780387226, 'tech_param_4_4': 4.212091370332146, 'tech_param_4_5': 6.790648339845854, 'tech_param_4_6': 9.793540661498884}. Best is trial 0 with value: 0.9.\n", - "[I 2023-11-18 12:13:07,804] Trial 5 finished with value: 0.9 and parameters: {'tech_param_0_0': 3.7955449152390734, 'tech_param_0_1': 3.5879051604064425, 'tech_param_0_2': 5.047936105941167, 'tech_param_0_3': 6.409387696471882, 'tech_param_0_4': 6.139796193716612, 'tech_param_0_5': 5.818035000269156, 'tech_param_0_6': 6.77671788266593, 'tech_param_1_0': 3.538002063564025, 'tech_param_1_1': 4.653916400756065, 'tech_param_1_2': 5.139067082232518, 'tech_param_1_3': 5.258612796259207, 'tech_param_1_4': 5.762995511794475, 'tech_param_1_5': 5.914412902115752, 'tech_param_1_6': 8.821608430328544, 'tech_param_2_0': 2.8391352600362043, 'tech_param_2_1': 1.3147834382265242, 'tech_param_2_2': 5.712530324883787, 'tech_param_2_3': 3.9982127746896365, 'tech_param_2_4': 7.59784603286235, 'tech_param_2_5': 8.249304067800995, 'tech_param_2_6': 9.13797124898645, 'tech_param_3_0': 3.4218288637879235, 'tech_param_3_1': 2.8432006830690506, 'tech_param_3_2': 3.705582741959596, 'tech_param_3_3': 4.932782214722957, 'tech_param_3_4': 5.397522930636624, 'tech_param_3_5': 7.665389302007817, 'tech_param_3_6': 8.161440460609116, 'tech_param_4_0': 0.9002808033064547, 'tech_param_4_1': 2.8214804227095938, 'tech_param_4_2': 4.209001287953269, 'tech_param_4_3': 3.4276241669947267, 'tech_param_4_4': 5.65920280643175, 'tech_param_4_5': 7.0296445059400385, 'tech_param_4_6': 6.58394027927913}. Best is trial 0 with value: 0.9.\n", - "[I 2023-11-18 12:13:08,436] Trial 6 finished with value: 0.9 and parameters: {'tech_param_0_0': 2.1661335557317467, 'tech_param_0_1': 2.10482781209877, 'tech_param_0_2': 3.5267055048813605, 'tech_param_0_3': 3.3403923781628193, 'tech_param_0_4': 7.251793183308415, 'tech_param_0_5': 5.419077835893116, 'tech_param_0_6': 6.92337803745288, 'tech_param_1_0': 2.248588320589992, 'tech_param_1_1': 1.5496877038644339, 'tech_param_1_2': 3.034290526391054, 'tech_param_1_3': 5.70430130433426, 'tech_param_1_4': 4.755971043039262, 'tech_param_1_5': 6.032107463009746, 'tech_param_1_6': 6.485937845252712, 'tech_param_2_0': 1.1198667631159256, 'tech_param_2_1': 4.443832638453198, 'tech_param_2_2': 4.029162583096888, 'tech_param_2_3': 4.421249989146505, 'tech_param_2_4': 7.266267320462586, 'tech_param_2_5': 7.272268040989632, 'tech_param_2_6': 9.34608451054512, 'tech_param_3_0': 3.5400627448638406, 'tech_param_3_1': 1.0291771457580832, 'tech_param_3_2': 3.001266350018537, 'tech_param_3_3': 3.83986701735892, 'tech_param_3_4': 4.21380354332598, 'tech_param_3_5': 5.7510339129863555, 'tech_param_3_6': 9.666174111722901, 'tech_param_4_0': 0.25936572770098243, 'tech_param_4_1': 1.0411384740076457, 'tech_param_4_2': 3.6068625395816722, 'tech_param_4_3': 4.778931228821068, 'tech_param_4_4': 5.259093795072825, 'tech_param_4_5': 8.171709511787906, 'tech_param_4_6': 9.580347111957641}. Best is trial 0 with value: 0.9.\n" - ] - } - ], - "source": [ - "import optuna\n", - "import numpy as np\n", - "\n", - "def cost_function(x, y):\n", - " return 0.9\n", - "\n", - "def tpe_objective_fn(trial):\n", - " num_steps = 5\n", - " num_heat_sources = 7\n", - " lb = [0, 1, 2, 3, 4, 5, 6]\n", - " ub = [4, 5, 6, 7, 8, 9, 10]\n", - " chosen_parameters = []\n", - " for step in range(num_steps):\n", - " for idx in range(num_heat_sources):\n", - " chosen_parameters.append(\n", - " trial.suggest_float(f\"tech_param_{step}_{idx}\", lb[idx], ub[idx])\n", - " )\n", - "\n", - " cost = cost_function(\n", - " chosen_parameters,\n", - " num_steps,\n", - " )\n", - " return cost\n", - "\n", - "if __name__ == \"__main__\":\n", - " study = optuna.create_study(\n", - " storage=\"sqlite:///db.sqlite3\", # Specify the storage URL here.\n", - " study_name=\"mdbugb\"\n", - " )\n", - " study.optimize(tpe_objective_fn, n_trials=7)" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "2c66fec2-3e46-46c4-bc55-e784d237a3e5", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} diff --git a/Untitled.ipynb b/Untitled.ipynb deleted file mode 100644 index 2307d508..00000000 --- a/Untitled.ipynb +++ /dev/null @@ -1,85 +0,0 @@ -{ - "cells": [ - { - "cell_type": "code", - "execution_count": 1, - "id": "c45a38b9-08a7-4ad1-85eb-722b50240f2d", - "metadata": {}, - "outputs": [ - { - "name": "stderr", - "output_type": "stream", - "text": [ - "C:\\Users\\victo\\AppData\\Local\\Programs\\Python\\Python311\\Lib\\site-packages\\tqdm\\auto.py:21: TqdmWarning: IProgress not found. Please update jupyter and ipywidgets. See https://ipywidgets.readthedocs.io/en/stable/user_install.html\n", - " from .autonotebook import tqdm as notebook_tqdm\n", - "[I 2023-10-21 17:21:34,373] A new study created in RDB with name: quadratic-simple3\n", - "[I 2023-10-21 17:21:34,461] Trial 0 finished with value: 4357.102948257252 and parameters: {'x': 66.0007799064318, 'y': 1}. Best is trial 0 with value: 4357.102948257252.\n", - "[W 2023-10-21 17:21:34,508] Trial 1 failed with parameters: {'x': -21.941587885601493, 'y': -1} because of the following error: The value None could not be cast to float..\n", - "[W 2023-10-21 17:21:34,509] Trial 1 failed with value None.\n", - "[I 2023-10-21 17:21:34,566] Trial 2 finished with value: 2536.6789394289817 and parameters: {'x': 50.37538029066363, 'y': -1}. Best is trial 2 with value: 2536.6789394289817.\n", - "[W 2023-10-21 17:21:34,612] Trial 3 failed with parameters: {'x': -96.83015025022239, 'y': 0} because of the following error: The value None could not be cast to float..\n", - "[W 2023-10-21 17:21:34,613] Trial 3 failed with value None.\n", - "[I 2023-10-21 17:21:34,670] Trial 4 finished with value: 3591.471500742954 and parameters: {'x': -59.920543228036195, 'y': 1}. Best is trial 2 with value: 2536.6789394289817.\n", - "[W 2023-10-21 17:21:34,717] Trial 5 failed with parameters: {'x': -65.11319825521136, 'y': 1} because of the following error: The value None could not be cast to float..\n", - "[W 2023-10-21 17:21:34,717] Trial 5 failed with value None.\n", - "[I 2023-10-21 17:21:34,769] Trial 6 finished with value: 9478.234748213168 and parameters: {'x': -97.35622603723486, 'y': 0}. Best is trial 2 with value: 2536.6789394289817.\n" - ] - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "Best value: 2536.6789394289817 (params: {'x': 50.37538029066363, 'y': -1})\n" - ] - } - ], - "source": [ - "import optuna\n", - "\n", - "def objective(trial):\n", - " x = trial.suggest_float(\"x\", -100, 100)\n", - " y = trial.suggest_categorical(\"y\", [-1, 0, 1])\n", - " if trial._trial_id % 2:\n", - " return None\n", - " return x**2 + y\n", - "\n", - "if __name__ == \"__main__\":\n", - " study = optuna.create_study(\n", - " storage=\"sqlite:///db.sqlite3\", # Specify the storage URL here.\n", - " study_name=\"quadratic-simple3\"\n", - " )\n", - " study.optimize(objective, n_trials=7)\n", - " print(f\"Best value: {study.best_value} (params: {study.best_params})\")" - ] - }, - { - "cell_type": "code", - "execution_count": null, - "id": "fedfab0b-c074-4d94-b63f-e6ee7df8e9e0", - "metadata": {}, - "outputs": [], - "source": [] - } - ], - "metadata": { - "kernelspec": { - "display_name": "Python 3 (ipykernel)", - "language": "python", - "name": "python3" - }, - "language_info": { - "codemirror_mode": { - "name": "ipython", - "version": 3 - }, - "file_extension": ".py", - "mimetype": "text/x-python", - "name": "python", - "nbconvert_exporter": "python", - "pygments_lexer": "ipython3", - "version": "3.11.4" - } - }, - "nbformat": 4, - "nbformat_minor": 5 -} From 60888843dba4bac175082b130947b1eecf05c5bd Mon Sep 17 00:00:00 2001 From: Contramundum Date: Wed, 29 Nov 2023 17:22:14 +0900 Subject: [PATCH 54/99] Add test on upload_artifact --- python_tests/artifact/test_backend.py | 62 +++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 14a470e3..0bb662aa 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -1,3 +1,5 @@ +import base64 +import json import tempfile from unittest.mock import MagicMock @@ -177,3 +179,63 @@ def test_successful_trial_artifact_retrieval() -> None: ) assert status == 200 assert body == b"dummy_content" + + +DUMMY_DATA = f"data:text/plain; charset=utf-8,{base64.b64encode(b'dummy_content').decode('utf-8')}" + + +def test_upload_artifact_invalid() -> None: + storage = optuna.storages.InMemoryStorage() + + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + + app = create_app(storage, artifact_store) + study = optuna.create_study(storage=storage) + + # Invalid: no trial + status, _, body = send_request( + app, + f"/api/artifacts/{study._study_id}/0", + "POST", + body=json.dumps({"file": DUMMY_DATA}), + content_type="application/json", + ) + assert status == 500 # TODO: This should return 400 + + # Invalid: complete trial + study.add_trial(optuna.create_trial(value=1.0, distributions={}, params={})) + trial = study.trials[-1] + status, _, body = send_request( + app, + f"/api/artifacts/{study._study_id}/{trial._trial_id}", + "POST", + body=json.dumps({"file": DUMMY_DATA}), + content_type="application/json", + ) + assert status == 400 + + +def test_upload_artifact() -> None: + storage = optuna.storages.InMemoryStorage() + + study = optuna.create_study(storage=storage) + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + + app = create_app(storage, artifact_store) + + study.add_trial(optuna.create_trial(state=optuna.trial.TrialState.RUNNING)) + trial = study.trials[-1] + status, _, body = send_request( + app, + f"/api/artifacts/{study._study_id}/{trial._trial_id}", + "POST", + body=json.dumps({"file": DUMMY_DATA}), + content_type="application/json", + ) + assert status == 201 + res = json.loads(body) + with open(f"{tmpdir}/{res['artifact_id']}", "r") as f: + data = f.read() + assert data == "dummy_content" From 019859e20b52b7efa697033838c71bd65fe83ed2 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 29 Nov 2023 18:16:23 +0900 Subject: [PATCH 55/99] Use WaveSurfer instead of audio tag. --- .../ts/components/ArtifactCardMedia.tsx | 10 ++- .../components/WaveSurferArtifactViewer.tsx | 79 +++++++++++++++++++ package-lock.json | 13 ++- package.json | 3 +- 4 files changed, 100 insertions(+), 5 deletions(-) create mode 100644 optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx diff --git a/optuna_dashboard/ts/components/ArtifactCardMedia.tsx b/optuna_dashboard/ts/components/ArtifactCardMedia.tsx index 282b7432..fecef837 100644 --- a/optuna_dashboard/ts/components/ArtifactCardMedia.tsx +++ b/optuna_dashboard/ts/components/ArtifactCardMedia.tsx @@ -3,6 +3,7 @@ import { ThreejsArtifactViewer, isThreejsArtifact, } from "./ThreejsArtifactViewer" +import { WaveSurferArtifactViewer } from "./WaveSurferArtifactViewer" import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile" import { CardMedia, Box } from "@mui/material" @@ -43,9 +44,12 @@ export const ArtifactCardMedia: FC<{ alignItems: "center", }} > - + ) } else if (artifact.mimetype.startsWith("image")) { diff --git a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx new file mode 100644 index 00000000..44dc6455 --- /dev/null +++ b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx @@ -0,0 +1,79 @@ +import React, { useCallback, useEffect, useState, useRef } from "react" +import WaveSurfer from "wavesurfer.js" +import { Box } from "@mui/material" + +interface WaveSurferArtifactViewerProps { + height: number + waveColor: string + progressColor: string + url: string +} + +// WaveSurfer hook +const useWavesurfer = ( + containerRef: React.MutableRefObject, + options: WaveSurferArtifactViewerProps +) => { + const [wavesurfer, setWavesurfer] = useState(null) + + // Initialize wavesurfer when the container mounts + // or any of the props change + useEffect(() => { + if (!containerRef.current) return + + const ws = WaveSurfer.create({ + ...options, + container: containerRef.current, + }) + + setWavesurfer(ws) + + return () => { + ws.destroy() + } + }, [options, containerRef]) + + return wavesurfer +} + +// Create a React component that will render wavesurfer. +// Props are wavesurfer options. +export const WaveSurferArtifactViewer: React.FC< + WaveSurferArtifactViewerProps +> = (props) => { + const containerRef = useRef(null!) + const [isPlaying, setIsPlaying] = useState(false) + const wavesurfer = useWavesurfer(containerRef, props) + + // On play button click + const onPlayClick = useCallback(() => { + if (!wavesurfer) return + wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play() + }, [wavesurfer]) + + // Initialize wavesurfer when the container mounts + // or any of the props change + useEffect(() => { + if (!wavesurfer) return + + setIsPlaying(false) + + const subscriptions = [ + wavesurfer.on("play", () => setIsPlaying(true)), + wavesurfer.on("pause", () => setIsPlaying(false)), + ] + + return () => { + subscriptions.forEach((unsub) => unsub()) + } + }, [wavesurfer]) + + return ( + +
+ + + ) +} diff --git a/package-lock.json b/package-lock.json index 409d4225..05c79bb0 100644 --- a/package-lock.json +++ b/package-lock.json @@ -32,7 +32,8 @@ "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", - "three": "^0.155.0" + "three": "^0.155.0", + "wavesurfer.js": "^7.4.12" }, "devDependencies": { "@babel/core": "^7.14.3", @@ -14824,6 +14825,11 @@ "node": ">=10.13.0" } }, + "node_modules/wavesurfer.js": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.4.12.tgz", + "integrity": "sha512-KzH4LkcOp8LECs9cOVIPBl6vsSoICKuZz+v5kh/zvxilpaVszU+QKC+4s2KEAqcCxBCecg3cNSg4RqAx278F8g==" + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -25938,6 +25944,11 @@ "graceful-fs": "^4.1.2" } }, + "wavesurfer.js": { + "version": "7.4.12", + "resolved": "https://registry.npmjs.org/wavesurfer.js/-/wavesurfer.js-7.4.12.tgz", + "integrity": "sha512-KzH4LkcOp8LECs9cOVIPBl6vsSoICKuZz+v5kh/zvxilpaVszU+QKC+4s2KEAqcCxBCecg3cNSg4RqAx278F8g==" + }, "web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", diff --git a/package.json b/package.json index 3e624c56..9e6e59e9 100644 --- a/package.json +++ b/package.json @@ -41,7 +41,8 @@ "rehype-raw": "^6.1.1", "remark-gfm": "^3.0.1", "remark-math": "^5.1.1", - "three": "^0.155.0" + "three": "^0.155.0", + "wavesurfer.js": "^7.4.12" }, "devDependencies": { "@babel/core": "^7.14.3", From dfc699e69cc8faf4fadfe83d77e4a52829e18ad5 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 29 Nov 2023 18:25:00 +0900 Subject: [PATCH 56/99] Update code comments. --- .../ts/components/WaveSurferArtifactViewer.tsx | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx index 44dc6455..65d87bfb 100644 --- a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx +++ b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx @@ -9,15 +9,12 @@ interface WaveSurferArtifactViewerProps { url: string } -// WaveSurfer hook const useWavesurfer = ( containerRef: React.MutableRefObject, options: WaveSurferArtifactViewerProps ) => { const [wavesurfer, setWavesurfer] = useState(null) - // Initialize wavesurfer when the container mounts - // or any of the props change useEffect(() => { if (!containerRef.current) return @@ -36,8 +33,7 @@ const useWavesurfer = ( return wavesurfer } -// Create a React component that will render wavesurfer. -// Props are wavesurfer options. +// Create a React component of wavesurfer. export const WaveSurferArtifactViewer: React.FC< WaveSurferArtifactViewerProps > = (props) => { @@ -45,14 +41,11 @@ export const WaveSurferArtifactViewer: React.FC< const [isPlaying, setIsPlaying] = useState(false) const wavesurfer = useWavesurfer(containerRef, props) - // On play button click const onPlayClick = useCallback(() => { if (!wavesurfer) return wavesurfer.isPlaying() ? wavesurfer.pause() : wavesurfer.play() }, [wavesurfer]) - // Initialize wavesurfer when the container mounts - // or any of the props change useEffect(() => { if (!wavesurfer) return From caa2d0cbbd185737ea7f4dbe8dd1268152c24aa4 Mon Sep 17 00:00:00 2001 From: Victoria A <52001888+adjeiv@users.noreply.github.com> Date: Wed, 29 Nov 2023 22:07:38 +0000 Subject: [PATCH 57/99] Move line so any exceptions are caught --- optuna_dashboard/_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index f10d051f..b91ce5f0 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -152,6 +152,7 @@ def create_app( storage=storage, study_name=dst_study_name, directions=src_study.directions ) dst_study.add_trials(src_study.get_trials(deepcopy=False)) + note.copy_notes(storage, src_study, dst_study) except DuplicatedStudyError: response.status = 400 # Bad request return {"reason": f"study_name={dst_study_name} is duplicaated"} @@ -165,7 +166,6 @@ def create_app( response.status = 500 return {"reason": "Failed to load the new study"} - note.copy_notes(storage, src_study, dst_study) storage.delete_study(src_study._study_id) response.status = 201 return serialize_study_summary(new_study_summary) From bee7c97009d10ec07a3c7d6af222b3dfd7a860ba Mon Sep 17 00:00:00 2001 From: Naoto Mizuno Date: Thu, 30 Nov 2023 15:24:16 +0900 Subject: [PATCH 58/99] Fix sort in rank plot --- optuna_dashboard/ts/components/GraphRank.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index 6781793f..a0ffd6ed 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -274,7 +274,7 @@ const getColors = (values: number[]): number[] => { } const getOrderWithSameOrderAveraging = (values: number[]): number[] => { - const sortedValues = values.slice().sort() + const sortedValues = values.slice().sort((a, b) => a - b) const ranks: number[] = [] values.forEach((value) => { const firstIndex = sortedValues.indexOf(value) From 1f29783877da2feab59f24808ce0017a5701046b Mon Sep 17 00:00:00 2001 From: contramundum53 Date: Thu, 30 Nov 2023 16:46:08 +0900 Subject: [PATCH 59/99] Update python_tests/artifact/test_backend.py Co-authored-by: c-bata --- python_tests/artifact/test_backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 0bb662aa..35caad85 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -201,7 +201,7 @@ def test_upload_artifact_invalid() -> None: body=json.dumps({"file": DUMMY_DATA}), content_type="application/json", ) - assert status == 500 # TODO: This should return 400 + assert status == 500 # TODO(contramundum53): This should return 400 # Invalid: complete trial study.add_trial(optuna.create_trial(value=1.0, distributions={}, params={})) From 5bbb11cf4654116fef9d3669e046666d8a00ecf0 Mon Sep 17 00:00:00 2001 From: Contramundum Date: Mon, 4 Dec 2023 16:10:55 +0900 Subject: [PATCH 60/99] code fix --- python_tests/artifact/test_backend.py | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index 0bb662aa..76627131 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -181,10 +181,10 @@ def test_successful_trial_artifact_retrieval() -> None: assert body == b"dummy_content" -DUMMY_DATA = f"data:text/plain; charset=utf-8,{base64.b64encode(b'dummy_content').decode('utf-8')}" +DUMMY_DATA_URL = f"data:text/plain; charset=utf-8,{base64.b64encode(b'dummy_content').decode('utf-8')}" -def test_upload_artifact_invalid() -> None: +def test_upload_artifact_invalid_no_trial() -> None: storage = optuna.storages.InMemoryStorage() with tempfile.TemporaryDirectory() as tmpdir: @@ -193,24 +193,31 @@ def test_upload_artifact_invalid() -> None: app = create_app(storage, artifact_store) study = optuna.create_study(storage=storage) - # Invalid: no trial status, _, body = send_request( app, f"/api/artifacts/{study._study_id}/0", "POST", - body=json.dumps({"file": DUMMY_DATA}), + body=json.dumps({"file": DUMMY_DATA_URL}), content_type="application/json", ) assert status == 500 # TODO: This should return 400 - # Invalid: complete trial +def test_upload_artifact_invalid_complete_trial() -> None: + storage = optuna.storages.InMemoryStorage() + + with tempfile.TemporaryDirectory() as tmpdir: + artifact_store = FileSystemArtifactStore(tmpdir) + + app = create_app(storage, artifact_store) + study = optuna.create_study(storage=storage) + study.add_trial(optuna.create_trial(value=1.0, distributions={}, params={})) trial = study.trials[-1] status, _, body = send_request( app, f"/api/artifacts/{study._study_id}/{trial._trial_id}", "POST", - body=json.dumps({"file": DUMMY_DATA}), + body=json.dumps({"file": DUMMY_DATA_URL}), content_type="application/json", ) assert status == 400 @@ -231,7 +238,7 @@ def test_upload_artifact() -> None: app, f"/api/artifacts/{study._study_id}/{trial._trial_id}", "POST", - body=json.dumps({"file": DUMMY_DATA}), + body=json.dumps({"file": DUMMY_DATA_URL}), content_type="application/json", ) assert status == 201 From 9e314a96ddb57a87cd3243fe987361a6a88402ed Mon Sep 17 00:00:00 2001 From: Contramundum Date: Mon, 4 Dec 2023 16:12:29 +0900 Subject: [PATCH 61/99] format --- python_tests/artifact/test_backend.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python_tests/artifact/test_backend.py b/python_tests/artifact/test_backend.py index f2192f03..52fe6027 100644 --- a/python_tests/artifact/test_backend.py +++ b/python_tests/artifact/test_backend.py @@ -181,7 +181,9 @@ def test_successful_trial_artifact_retrieval() -> None: assert body == b"dummy_content" -DUMMY_DATA_URL = f"data:text/plain; charset=utf-8,{base64.b64encode(b'dummy_content').decode('utf-8')}" +DUMMY_DATA_URL = ( + f"data:text/plain; charset=utf-8,{base64.b64encode(b'dummy_content').decode('utf-8')}" +) def test_upload_artifact_invalid_no_trial() -> None: @@ -202,6 +204,7 @@ def test_upload_artifact_invalid_no_trial() -> None: ) assert status == 500 # TODO(contramundum53): This should return 400 + def test_upload_artifact_invalid_complete_trial() -> None: storage = optuna.storages.InMemoryStorage() From 547d985a9fe3d1634928e34f0b89374a4fd681d1 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 11:37:58 +0900 Subject: [PATCH 62/99] Update _app.py --- optuna_dashboard/_app.py | 51 +++++++++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 16 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 8baa1280..d9d04666 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -2,7 +2,7 @@ from __future__ import annotations import csv import functools -import io +import io import logging import os import typing @@ -453,38 +453,57 @@ def create_app( @app.get("/csv/") def download_csv(study_id: int) -> BottleViewReturn: - # TODO: Create a CSV file + # Create a CSV file summary = get_study_summary(storage, study_id) if summary is None: response.status = 404 # Not found return {"reason": f"study_id={study_id} is not found"} trials = get_trials(storage, study_id) - param_names = list(trials[0].params.keys()) - union_user_attrs = list(trials[0].user_attrs) - column_names = ["Number", "State", "Value"] + param_names + union_user_attrs + param_names = [] + user_attr_names = [] + for trial in trials: + for param_name in trial.params.keys(): + if param_name not in param_names: + param_names.append(param_name) + for attr_name in trial.user_attrs.keys(): + if attr_name not in user_attr_names: + user_attr_names.append(attr_name) + + param_names_heading = [f"Param {x}" for x in param_names] + user_attr_names_heading = [f"UserAttribute {x}" for x in user_attr_names] + value_heading = ["Value"] + if len(trials[0].values) > 1: + value_heading = [f"Objective {x}" for x in range(len(trials[0].values))] + column_names = ( + ["Number", "State"] + value_heading + param_names_heading + user_attr_names_heading + ) buf = io.StringIO("") writer = csv.writer(buf) writer.writerow(column_names) for frozen_trial in trials: - row = [ - frozen_trial.number, - frozen_trial.state, - frozen_trial.values[0] - ] - row += [frozen_trial.params[param] for param in param_names] - row += [frozen_trial.user_attrs[attr] for attr in union_user_attrs] + row = [frozen_trial.number, frozen_trial.state.name] + row += frozen_trial.values + for param_name in param_names: + if param_name in frozen_trial.params.keys(): + row += [frozen_trial.params[param_name]] + else: + row += [None] + for attr_name in user_attr_names: + if attr_name in frozen_trial.user_attrs.keys(): + row += [frozen_trial.user_attrs[attr_name]] + else: + row += [None] writer.writerow(row) - - # TODO: Set response headers + + # Set response headers response.headers["Content-Type"] = "text/csv; chatset=cp932" response.headers["Content-Disposition"] = f"attachment; filename=trials_{study_id}.csv" - # TODO: Response body + # Response body buf.seek(0) return buf.read() - @app.get("/favicon.ico") def favicon() -> BottleViewReturn: From 65d7c8e4cc62818a4c304bad692b5c75652b05ef Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 11:39:12 +0900 Subject: [PATCH 63/99] add download button --- .../ts/components/StudyDetail.tsx | 48 +++++++++++++------ 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index f85d7458..ce7fe3ce 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -150,21 +150,39 @@ export const StudyDetail: FC<{ content = } else if (page === "trialTable") { content = ( - - - - - - - - + + + + + + + + Download CSV File + {" "} + + + + + + + + ) } else if (page === "note" && studyDetail !== null) { content = ( From 46ec27719566ae6c04462aec8e05d5b5aa9382e1 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 11:47:07 +0900 Subject: [PATCH 64/99] fix lint --- optuna_dashboard/_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index d9d04666..b2b13b67 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -450,7 +450,7 @@ def create_app( note.save_note_with_version(storage, study_id, trial_id, req_note_ver, req_note_body) response.status = 204 # No content return {} - + @app.get("/csv/") def download_csv(study_id: int) -> BottleViewReturn: # Create a CSV file From d18562f62c836308107a0bb3e8ba25b6277c9d48 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Wed, 6 Dec 2023 07:02:17 +0100 Subject: [PATCH 65/99] [bug] Fix an error caused by infinity in trial.values --- optuna_dashboard/ts/components/GraphRank.tsx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index 7164b99b..cefe3145 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -152,13 +152,17 @@ const getRankPlotInfo = ( const zValues: number[] = [] const isFeasible: boolean[] = [] const hovertext: string[] = [] + const convertTrialValueToNumber = (value: TrialValueNumber): number => { + // TrialValueNumber takes `number`, "inf", or "-inf". + return typeof(value) === "number" ? value : value.includes("-") ? -Infinity : Infinity + }; filteredTrials.forEach((trial, i) => { const xValue = xAxis.values[i] const yValue = yAxis.values[i] if (xValue && yValue && trial.values) { xValues.push(xValue) yValues.push(yValue) - const zValue = Number(trial.values[objectiveId]) + const zValue = convertTrialValueToNumber(trial.values[objectiveId]) zValues.push(zValue) const feasibility = trial.constraints.every((c) => c <= 0) isFeasible.push(feasibility) From bdd27590a97a69216942d0d6e25b569fe5d3ba13 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Wed, 6 Dec 2023 07:12:07 +0100 Subject: [PATCH 66/99] Apply formatter --- optuna_dashboard/ts/components/GraphRank.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphRank.tsx b/optuna_dashboard/ts/components/GraphRank.tsx index cefe3145..6daaf10a 100644 --- a/optuna_dashboard/ts/components/GraphRank.tsx +++ b/optuna_dashboard/ts/components/GraphRank.tsx @@ -154,8 +154,12 @@ const getRankPlotInfo = ( const hovertext: string[] = [] const convertTrialValueToNumber = (value: TrialValueNumber): number => { // TrialValueNumber takes `number`, "inf", or "-inf". - return typeof(value) === "number" ? value : value.includes("-") ? -Infinity : Infinity - }; + return typeof value === "number" + ? value + : value.includes("-") + ? -Infinity + : Infinity + } filteredTrials.forEach((trial, i) => { const xValue = xAxis.values[i] const yValue = yAxis.values[i] From 6f681c40304c27b26066d513e645098284d95ddb Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 15:25:57 +0900 Subject: [PATCH 67/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index b2b13b67..c0476268 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -483,18 +483,9 @@ def create_app( writer = csv.writer(buf) writer.writerow(column_names) for frozen_trial in trials: - row = [frozen_trial.number, frozen_trial.state.name] - row += frozen_trial.values - for param_name in param_names: - if param_name in frozen_trial.params.keys(): - row += [frozen_trial.params[param_name]] - else: - row += [None] - for attr_name in user_attr_names: - if attr_name in frozen_trial.user_attrs.keys(): - row += [frozen_trial.user_attrs[attr_name]] - else: - row += [None] + row = [frozen_trial.number, frozen_trial.state.name] + frozen_trial.values + row.extend([frozen_trial.params.get(name, None) for name in param_names]) + row.extend([frozen_trial.user_attrs.get(name, None) for name in user_attr_names]) writer.writerow(row) # Set response headers From 3b3142209982034f0f7f38fc0db2e4bd2eebbb53 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 15:28:19 +0900 Subject: [PATCH 68/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index c0476268..db50e1f4 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -460,15 +460,8 @@ def create_app( return {"reason": f"study_id={study_id} is not found"} trials = get_trials(storage, study_id) - param_names = [] - user_attr_names = [] - for trial in trials: - for param_name in trial.params.keys(): - if param_name not in param_names: - param_names.append(param_name) - for attr_name in trial.user_attrs.keys(): - if attr_name not in user_attr_names: - user_attr_names.append(attr_name) + param_names = sorted(set(chain.from_iterable([t.params.keys() for t in trials]))) + user_attr_names = sorted(set(chain.from_iterable([t.user_attrs.keys() for t in trials]))) param_names_heading = [f"Param {x}" for x in param_names] user_attr_names_heading = [f"UserAttribute {x}" for x in user_attr_names] From a5502cefc21f3ac1333d8f642acb0a7687b11fe4 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 15:31:04 +0900 Subject: [PATCH 69/99] Update _app.py --- optuna_dashboard/_app.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index db50e1f4..0af6756d 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -3,6 +3,7 @@ from __future__ import annotations import csv import functools import io +from itertools import chain import logging import os import typing @@ -463,13 +464,13 @@ def create_app( param_names = sorted(set(chain.from_iterable([t.params.keys() for t in trials]))) user_attr_names = sorted(set(chain.from_iterable([t.user_attrs.keys() for t in trials]))) - param_names_heading = [f"Param {x}" for x in param_names] - user_attr_names_heading = [f"UserAttribute {x}" for x in user_attr_names] - value_heading = ["Value"] + param_names_header = [f"Param {x}" for x in param_names] + user_attr_names_header = [f"UserAttribute {x}" for x in user_attr_names] + value_header = ["Value"] if len(trials[0].values) > 1: - value_heading = [f"Objective {x}" for x in range(len(trials[0].values))] + value_header = [f"Objective {x}" for x in range(len(trials[0].values))] column_names = ( - ["Number", "State"] + value_heading + param_names_heading + user_attr_names_heading + ["Number", "State"] + value_header + param_names_header + user_attr_names_header ) buf = io.StringIO("") From c73abacef3c96569638b120d28a7965f220c79c6 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 15:35:35 +0900 Subject: [PATCH 70/99] Add files via upload --- python_tests/test_csv_download.py | 73 +++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) create mode 100644 python_tests/test_csv_download.py diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py new file mode 100644 index 00000000..a820f37b --- /dev/null +++ b/python_tests/test_csv_download.py @@ -0,0 +1,73 @@ +import optuna +from optuna_dashboard._app import create_app +import pytest + +from .wsgi_client import send_request + + +@pytest.mark.parametrize("id", [0, 1]) +def test_download_csv_fail(id: int) -> None: + def objective(trial: optuna.Trial) -> float: + x = trial.suggest_float("x", -100, 100) + y = trial.suggest_categorical("y", [-1, 0, 1]) + return x**2 + y + + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + optuna.logging.set_verbosity(optuna.logging.ERROR) + study.optimize(objective, n_trials=10) + app = create_app(storage) + status, _, body = send_request( + app, + f"/csv/{id}", + "GET", + content_type="application/json", + ) + assert status == (404 if id != 0 else 200) + + +@pytest.mark.parametrize("is_multi_obj", [True, False]) +def test_download_csv_multi_obj(is_multi_obj: bool) -> None: + def objective(trial: optuna.Trial) -> float: + x = trial.suggest_float("x", -100, 100) + y = trial.suggest_categorical("y", [-1, 0, 1]) + if is_multi_obj: + return x**2, y + return x**2 + y + + storage = optuna.storages.InMemoryStorage() + if is_multi_obj: + study = optuna.create_study(storage=storage, directions=["minimize", "minimize"]) + else: + study = optuna.create_study(storage=storage) + optuna.logging.set_verbosity(optuna.logging.ERROR) + study.optimize(objective, n_trials=10) + app = create_app(storage) + status, _, body = send_request( + app, + f"/csv/{0}", + "GET", + content_type="application/json", + ) + assert status == 200 + + +def test_download_csv_user_attr() -> None: + def objective(trial: optuna.Trial) -> float: + x = trial.suggest_float("x", -100, 100) + y = trial.suggest_categorical("y", [-1, 0, 1]) + trial.set_user_attr("abs_y", abs(y)) + return x**2 + y + + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + optuna.logging.set_verbosity(optuna.logging.ERROR) + study.optimize(objective, n_trials=10) + app = create_app(storage) + status, _, body = send_request( + app, + f"/csv/{0}", + "GET", + content_type="application/json", + ) + assert status == 200 From 26d29f3c02e03b1b51127a642485a25da83e1fe9 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 15:54:40 +0900 Subject: [PATCH 71/99] fix mypy --- python_tests/test_csv_download.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index a820f37b..de144fd0 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -1,3 +1,5 @@ +from typing import Any + import optuna from optuna_dashboard._app import create_app import pytest @@ -28,7 +30,7 @@ def test_download_csv_fail(id: int) -> None: @pytest.mark.parametrize("is_multi_obj", [True, False]) def test_download_csv_multi_obj(is_multi_obj: bool) -> None: - def objective(trial: optuna.Trial) -> float: + def objective(trial: optuna.Trial) -> Any: x = trial.suggest_float("x", -100, 100) y = trial.suggest_categorical("y", [-1, 0, 1]) if is_multi_obj: From 895fc0bd0489a3dc9693a5ecbcef7f192eeafad5 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 16:26:02 +0900 Subject: [PATCH 72/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 0af6756d..f6718945 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -460,6 +460,8 @@ def create_app( response.status = 404 # Not found return {"reason": f"study_id={study_id} is not found"} trials = get_trials(storage, study_id) + if len(trials) == 0: + return {"reason": f"study_id={study_id} has no trials"} param_names = sorted(set(chain.from_iterable([t.params.keys() for t in trials]))) user_attr_names = sorted(set(chain.from_iterable([t.user_attrs.keys() for t in trials]))) From 908788d029a6bc626eb4f1d69f6006c206160e9a Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 16:26:38 +0900 Subject: [PATCH 73/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index f6718945..7f323518 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -469,8 +469,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] value_header = ["Value"] - if len(trials[0].values) > 1: - value_header = [f"Objective {x}" for x in range(len(trials[0].values))] + n_objs = max([len(t.values) for t in trials if t.values is not None], default=1) + if n_objs > 1: + value_header = [f"Objective {x}" for x in range(n_objs)] column_names = ( ["Number", "State"] + value_header + param_names_header + user_attr_names_header ) From 2a03cd52855d3ff16a59e29fe5aae74f1e28c095 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 16:26:55 +0900 Subject: [PATCH 74/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 7f323518..ad4934e4 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -480,7 +480,8 @@ def create_app( writer = csv.writer(buf) writer.writerow(column_names) for frozen_trial in trials: - row = [frozen_trial.number, frozen_trial.state.name] + frozen_trial.values + row = [frozen_trial.number, frozen_trial.state.name] + row.extend(frozen_trial.values if frozen_trial.values is not None else [None] * n_objs) row.extend([frozen_trial.params.get(name, None) for name in param_names]) row.extend([frozen_trial.user_attrs.get(name, None) for name in user_attr_names]) writer.writerow(row) From 8eac8fc6f31e7420f0f5a277ebdd17953c7602be Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 16:36:00 +0900 Subject: [PATCH 75/99] add tests --- python_tests/test_csv_download.py | 52 +++++++++++++++++++++++++++++-- 1 file changed, 50 insertions(+), 2 deletions(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index de144fd0..b8491502 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -1,12 +1,60 @@ from typing import Any import optuna +from optuna.trial import TrialState from optuna_dashboard._app import create_app import pytest from .wsgi_client import send_request +def test_download_csv_no_trial() -> None: + def objective(trial: optuna.Trial) -> float: + x = trial.suggest_float("x", -100, 100) + y = trial.suggest_categorical("y", [-1, 0, 1]) + return x**2 + y + + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + study.optimize(objective, n_trials=0) + app = create_app(storage) + status, _, body = send_request( + app, + "/csv/0", + "GET", + content_type="application/json", + ) + assert status == 200 + + +def test_download_csv_all_waiting() -> None: + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + study.add_trial(optuna.trial.create_trial(state=TrialState.WAITING)) + app = create_app(storage) + status, _, body = send_request( + app, + "/csv/0", + "GET", + content_type="application/json", + ) + assert status == 200 + + +def test_download_csv_all_running() -> None: + storage = optuna.storages.InMemoryStorage() + study = optuna.create_study(storage=storage) + study.add_trial(optuna.trial.create_trial(state=TrialState.RUNNING)) + app = create_app(storage) + status, _, body = send_request( + app, + "/csv/0", + "GET", + content_type="application/json", + ) + assert status == 200 + + @pytest.mark.parametrize("id", [0, 1]) def test_download_csv_fail(id: int) -> None: def objective(trial: optuna.Trial) -> float: @@ -47,7 +95,7 @@ def test_download_csv_multi_obj(is_multi_obj: bool) -> None: app = create_app(storage) status, _, body = send_request( app, - f"/csv/{0}", + "/csv/0", "GET", content_type="application/json", ) @@ -68,7 +116,7 @@ def test_download_csv_user_attr() -> None: app = create_app(storage) status, _, body = send_request( app, - f"/csv/{0}", + "/csv/0", "GET", content_type="application/json", ) From 2fde74b00dca4b7ee7a62a1b73b220b2a1ab40f6 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 17:19:42 +0900 Subject: [PATCH 76/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index ad4934e4..8ddd9526 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -460,8 +460,6 @@ def create_app( response.status = 404 # Not found return {"reason": f"study_id={study_id} is not found"} trials = get_trials(storage, study_id) - if len(trials) == 0: - return {"reason": f"study_id={study_id} has no trials"} param_names = sorted(set(chain.from_iterable([t.params.keys() for t in trials]))) user_attr_names = sorted(set(chain.from_iterable([t.user_attrs.keys() for t in trials]))) From 3d6e69a16905f78b6b5221bb0e1dff05fe426b15 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 17:48:09 +0900 Subject: [PATCH 77/99] revised _app.py --- optuna_dashboard/_app.py | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 8ddd9526..a74ce1e9 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -6,6 +6,7 @@ import io from itertools import chain import logging import os +import re import typing from typing import Any from typing import Optional @@ -467,9 +468,12 @@ 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] value_header = ["Value"] - n_objs = max([len(t.values) for t in trials if t.values is not None], default=1) + n_objs = len(summary.directions) if n_objs > 1: - value_header = [f"Objective {x}" for x in range(n_objs)] + if "study:metric_names" in summary._system_attrs: + value_header = summary._system_attrs["study:metric_names"] + else: + value_header = [f"Objective {x}" for x in range(n_objs)] column_names = ( ["Number", "State"] + value_header + param_names_header + user_attr_names_header ) @@ -485,8 +489,9 @@ def create_app( writer.writerow(row) # Set response headers + output_filename = re.sub(r'[\\/:*?"<>|]+', "", summary.study_name) response.headers["Content-Type"] = "text/csv; chatset=cp932" - response.headers["Content-Disposition"] = f"attachment; filename=trials_{study_id}.csv" + response.headers["Content-Disposition"] = f"attachment; filename=trials_{output_filename}.csv" # Response body buf.seek(0) From e5e813bdde012379fbec3dbc229ce87c31fa96bf Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 17:56:00 +0900 Subject: [PATCH 78/99] fix lint --- optuna_dashboard/_app.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index a74ce1e9..115d4078 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -489,9 +489,9 @@ def create_app( writer.writerow(row) # Set response headers - output_filename = re.sub(r'[\\/:*?"<>|]+', "", summary.study_name) + output_name = re.sub(r'[\\/:*?"<>|]+', "", summary.study_name) response.headers["Content-Type"] = "text/csv; chatset=cp932" - response.headers["Content-Disposition"] = f"attachment; filename=trials_{output_filename}.csv" + response.headers["Content-Disposition"] = f"attachment; filename={output_name}.csv" # Response body buf.seek(0) From f57bab2d7d8a8bc3b897958148717c4764e914aa Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 18:05:15 +0900 Subject: [PATCH 79/99] revised _app.py --- optuna_dashboard/_app.py | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 115d4078..6a916c5d 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -456,24 +456,22 @@ def create_app( @app.get("/csv/") def download_csv(study_id: int) -> BottleViewReturn: # Create a CSV file - summary = get_study_summary(storage, study_id) - if summary is None: + try: + study_name = storage.get_study_name_from_id(study_id) + study = optuna.load_study(storage=storage, study_name=study_name) + except KeyError: response.status = 404 # Not found return {"reason": f"study_id={study_id} is not found"} - trials = get_trials(storage, study_id) - + trials = study.trials param_names = sorted(set(chain.from_iterable([t.params.keys() for t in trials]))) user_attr_names = sorted(set(chain.from_iterable([t.user_attrs.keys() for t in trials]))) - param_names_header = [f"Param {x}" for x in param_names] user_attr_names_header = [f"UserAttribute {x}" for x in user_attr_names] - value_header = ["Value"] - n_objs = len(summary.directions) - if n_objs > 1: - if "study:metric_names" in summary._system_attrs: - value_header = summary._system_attrs["study:metric_names"] - else: - value_header = [f"Objective {x}" for x in range(n_objs)] + n_objs = len(study.directions) + if study.metric_names is not None: + value_header = study.metric_names + else: + 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 ) @@ -489,7 +487,7 @@ def create_app( writer.writerow(row) # Set response headers - output_name = re.sub(r'[\\/:*?"<>|]+', "", summary.study_name) + output_name = re.sub(r'[\\/:*?"<>|]+', "", study_name) response.headers["Content-Type"] = "text/csv; chatset=cp932" response.headers["Content-Disposition"] = f"attachment; filename={output_name}.csv" From c9915ad12d3640741ffbc9e826689fdbe30d80f3 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 18:26:17 +0900 Subject: [PATCH 80/99] Update optuna_dashboard/_app.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- optuna_dashboard/_app.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/_app.py b/optuna_dashboard/_app.py index 6a916c5d..ead27188 100644 --- a/optuna_dashboard/_app.py +++ b/optuna_dashboard/_app.py @@ -487,7 +487,7 @@ def create_app( writer.writerow(row) # Set response headers - output_name = re.sub(r'[\\/:*?"<>|]+', "", study_name) + output_name = "-".join(re.sub(r'[\\/:*?"<>|]+', "", study_name).split(" ")) response.headers["Content-Type"] = "text/csv; chatset=cp932" response.headers["Content-Disposition"] = f"attachment; filename={output_name}.csv" From 7d834d3aaed6b4fc2078c52623b64f8986a9e94b Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Wed, 6 Dec 2023 18:36:56 +0900 Subject: [PATCH 81/99] Update StudyDetail.tsx --- .../ts/components/StudyDetail.tsx | 20 +++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index ce7fe3ce..2c464027 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -151,19 +151,11 @@ export const StudyDetail: FC<{ } else if (page === "trialTable") { content = ( - - - - - - - Download CSV File - {" "} + + Download CSV File + + + + + + ) } else if (page === "note" && studyDetail !== null) { From afe0299e4ffe6bdc68cddbbed17c76e2067a1368 Mon Sep 17 00:00:00 2001 From: Toshihiko Yanase Date: Wed, 6 Dec 2023 22:31:23 +0900 Subject: [PATCH 82/99] Do not observe options of wavesurfer. --- optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx index 65d87bfb..74177602 100644 --- a/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx +++ b/optuna_dashboard/ts/components/WaveSurferArtifactViewer.tsx @@ -28,7 +28,7 @@ const useWavesurfer = ( return () => { ws.destroy() } - }, [options, containerRef]) + }, [containerRef]) return wavesurfer } From 64dc46c0c2552159709dc3ac56ff49ac807f1008 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 10:30:09 +0900 Subject: [PATCH 83/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index b8491502..89f8978f 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -18,7 +18,7 @@ def test_download_csv_no_trial() -> None: study = optuna.create_study(storage=storage) study.optimize(objective, n_trials=0) app = create_app(storage) - status, _, body = send_request( + status, _, _ = send_request( app, "/csv/0", "GET", From 462bc99b339b49733852e7c8cd8e73f4df4ccb5f Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 10:31:31 +0900 Subject: [PATCH 84/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index 89f8978f..55cd4569 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -86,10 +86,8 @@ def test_download_csv_multi_obj(is_multi_obj: bool) -> None: return x**2 + y storage = optuna.storages.InMemoryStorage() - if is_multi_obj: - study = optuna.create_study(storage=storage, directions=["minimize", "minimize"]) - else: - study = optuna.create_study(storage=storage) + directions = ["minimize", "minimize"] if is_multi_obj else ["minimize"] + study = optuna.create_study(storage=storage, directions=directions) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) app = create_app(storage) From 678b15e60cc17ce5164e80610514fd60d1f3a477 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 10:31:55 +0900 Subject: [PATCH 85/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index 55cd4569..7740ea83 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -55,8 +55,8 @@ def test_download_csv_all_running() -> None: assert status == 200 -@pytest.mark.parametrize("id", [0, 1]) -def test_download_csv_fail(id: int) -> None: +@pytest.mark.parametrize("study_id", [0, 1]) +def test_download_csv_fail(study_id: int) -> None: def objective(trial: optuna.Trial) -> float: x = trial.suggest_float("x", -100, 100) y = trial.suggest_categorical("y", [-1, 0, 1]) From 18d68ea91959563d0364dc561ca4ee3576a1625a Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 10:58:16 +0900 Subject: [PATCH 86/99] Update test_csv_download.py --- python_tests/test_csv_download.py | 69 ++++++++++--------------------- 1 file changed, 21 insertions(+), 48 deletions(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index 7740ea83..e1ab12c9 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -8,6 +8,21 @@ import pytest from .wsgi_client import send_request +def _validate_output( + storage: optuna.storages.BaseStorage, + correct_status: int, + study_id: int, +) -> None: + app = create_app(storage) + status, _, _ = send_request( + app, + f"/csv/{study_id}", + "GET", + content_type="application/json", + ) + assert status == correct_status + + def test_download_csv_no_trial() -> None: def objective(trial: optuna.Trial) -> float: x = trial.suggest_float("x", -100, 100) @@ -17,42 +32,21 @@ def test_download_csv_no_trial() -> None: storage = optuna.storages.InMemoryStorage() study = optuna.create_study(storage=storage) study.optimize(objective, n_trials=0) - app = create_app(storage) - status, _, _ = send_request( - app, - "/csv/0", - "GET", - content_type="application/json", - ) - assert status == 200 + _validate_output(storage, 200, 0) def test_download_csv_all_waiting() -> None: storage = optuna.storages.InMemoryStorage() study = optuna.create_study(storage=storage) study.add_trial(optuna.trial.create_trial(state=TrialState.WAITING)) - app = create_app(storage) - status, _, body = send_request( - app, - "/csv/0", - "GET", - content_type="application/json", - ) - assert status == 200 + _validate_output(storage, 200, 0) def test_download_csv_all_running() -> None: storage = optuna.storages.InMemoryStorage() study = optuna.create_study(storage=storage) study.add_trial(optuna.trial.create_trial(state=TrialState.RUNNING)) - app = create_app(storage) - status, _, body = send_request( - app, - "/csv/0", - "GET", - content_type="application/json", - ) - assert status == 200 + _validate_output(storage, 200, 0) @pytest.mark.parametrize("study_id", [0, 1]) @@ -66,14 +60,7 @@ def test_download_csv_fail(study_id: int) -> None: study = optuna.create_study(storage=storage) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) - app = create_app(storage) - status, _, body = send_request( - app, - f"/csv/{id}", - "GET", - content_type="application/json", - ) - assert status == (404 if id != 0 else 200) + _validate_output(storage, 404 if study_id != 0 else 200, study_id) @pytest.mark.parametrize("is_multi_obj", [True, False]) @@ -90,14 +77,7 @@ def test_download_csv_multi_obj(is_multi_obj: bool) -> None: study = optuna.create_study(storage=storage, directions=directions) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) - app = create_app(storage) - status, _, body = send_request( - app, - "/csv/0", - "GET", - content_type="application/json", - ) - assert status == 200 + _validate_output(storage, 200, 0) def test_download_csv_user_attr() -> None: @@ -111,11 +91,4 @@ def test_download_csv_user_attr() -> None: study = optuna.create_study(storage=storage) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) - app = create_app(storage) - status, _, body = send_request( - app, - "/csv/0", - "GET", - content_type="application/json", - ) - assert status == 200 + _validate_output(storage, 200, 0) From 4a3849b07312d53f8205b8e18fb9a442971108b9 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 11:57:52 +0900 Subject: [PATCH 87/99] Update StudyDetail.tsx --- optuna_dashboard/ts/components/StudyDetail.tsx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index 2c464027..f7a6b0eb 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -157,8 +157,8 @@ export const StudyDetail: FC<{ width: "auto", height: "auto", display: "flex", - justifyContent: "center", - alignItems: "center", + justifyContent: "left", + alignItems: "left", }} > @@ -166,7 +166,6 @@ export const StudyDetail: FC<{ aria-label="download csv" size="small" color="inherit" - download={`trials_${studyDetail?.id}.csv`} sx={{ margin: "auto 0" }} href={`/csv/${studyDetail?.id}`} > From 6d442254e2019f85b38d9728de84134458b43216 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 12:06:38 +0900 Subject: [PATCH 88/99] Update StudyDetail.tsx --- optuna_dashboard/ts/components/StudyDetail.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/optuna_dashboard/ts/components/StudyDetail.tsx b/optuna_dashboard/ts/components/StudyDetail.tsx index f7a6b0eb..79702f2e 100644 --- a/optuna_dashboard/ts/components/StudyDetail.tsx +++ b/optuna_dashboard/ts/components/StudyDetail.tsx @@ -166,6 +166,7 @@ export const StudyDetail: FC<{ aria-label="download csv" size="small" color="inherit" + download sx={{ margin: "auto 0" }} href={`/csv/${studyDetail?.id}`} > From 885da8fcacedf09ecd4477bbeb1451c183591277 Mon Sep 17 00:00:00 2001 From: Naoto Mizuno Date: Thu, 7 Dec 2023 13:27:38 +0900 Subject: [PATCH 89/99] Prevent layout shift with intermediate values plot by rearranging the order of graphs --- .../ts/components/StudyHistory.tsx | 22 +++++++++---------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/optuna_dashboard/ts/components/StudyHistory.tsx b/optuna_dashboard/ts/components/StudyHistory.tsx index 4bcbbc0a..8fab513a 100644 --- a/optuna_dashboard/ts/components/StudyHistory.tsx +++ b/optuna_dashboard/ts/components/StudyHistory.tsx @@ -108,17 +108,6 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => { - {studyDetail !== null && - studyDetail.directions.length === 1 && - studyDetail.has_intermediate_values ? ( - - - - ) : null} = ({ studyId }) => { + {studyDetail !== null && + studyDetail.directions.length === 1 && + studyDetail.has_intermediate_values ? ( + + + + ) : null} {artifactEnabled && studyDetail !== null && ( From c0df7dc3d8ea792fb9b20f751e26a9206d21ef68 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Thu, 7 Dec 2023 07:46:52 +0100 Subject: [PATCH 90/99] Clear cache before going to another tests in unit tests --- e2e_tests/test_dashboard/visual_regression_test.py | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/e2e_tests/test_dashboard/visual_regression_test.py b/e2e_tests/test_dashboard/visual_regression_test.py index ea6ff930..876c585b 100644 --- a/e2e_tests/test_dashboard/visual_regression_test.py +++ b/e2e_tests/test_dashboard/visual_regression_test.py @@ -4,11 +4,22 @@ import optuna from playwright.sync_api import Page import pytest +from optuna_dashboard._storage import trials_cache +from optuna_dashboard._storage import trials_cache_lock +from optuna_dashboard._storage import trials_last_fetched_at + from ..test_server import make_test_server +def clear_inmemory_cache() -> None: + with trials_cache_lock: + trials_cache.clear() + trials_last_fetched_at.clear() + + @pytest.fixture def storage() -> optuna.storages.InMemoryStorage: + clear_inmemory_cache() storage = optuna.storages.InMemoryStorage() return storage From a5e2be25b54390dda882532c3cd227b0b04f74c3 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Thu, 7 Dec 2023 07:51:09 +0100 Subject: [PATCH 91/99] Apply isort --- e2e_tests/test_dashboard/visual_regression_test.py | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/e2e_tests/test_dashboard/visual_regression_test.py b/e2e_tests/test_dashboard/visual_regression_test.py index 876c585b..e5c3a992 100644 --- a/e2e_tests/test_dashboard/visual_regression_test.py +++ b/e2e_tests/test_dashboard/visual_regression_test.py @@ -1,12 +1,11 @@ from typing import Callable import optuna -from playwright.sync_api import Page -import pytest - from optuna_dashboard._storage import trials_cache from optuna_dashboard._storage import trials_cache_lock from optuna_dashboard._storage import trials_last_fetched_at +from playwright.sync_api import Page +import pytest from ..test_server import make_test_server From aa0fccc65ea68bdb5121312baa773d8949fbc791 Mon Sep 17 00:00:00 2001 From: nabenabe0928 Date: Thu, 7 Dec 2023 08:55:15 +0100 Subject: [PATCH 92/99] Clear cache for other tests as well --- .../test_usecases/test_preferential_optimization.py | 2 ++ .../test_dashboard/test_usecases/test_study_history.py | 2 ++ e2e_tests/test_dashboard/visual_regression_test.py | 10 +--------- e2e_tests/utils.py | 9 +++++++++ 4 files changed, 14 insertions(+), 9 deletions(-) create mode 100644 e2e_tests/utils.py diff --git a/e2e_tests/test_dashboard/test_usecases/test_preferential_optimization.py b/e2e_tests/test_dashboard/test_usecases/test_preferential_optimization.py index fa3317dc..0b19fea0 100644 --- a/e2e_tests/test_dashboard/test_usecases/test_preferential_optimization.py +++ b/e2e_tests/test_dashboard/test_usecases/test_preferential_optimization.py @@ -9,6 +9,7 @@ from playwright.sync_api import Page import pytest from ...test_server import make_test_server +from ...utils import clear_inmemory_cache def make_test_storage() -> optuna.storages.InMemoryStorage: @@ -43,6 +44,7 @@ def make_test_storage() -> optuna.storages.InMemoryStorage: @pytest.fixture def storage() -> optuna.storages.InMemoryStorage: + clear_inmemory_cache() storage = make_test_storage() return storage diff --git a/e2e_tests/test_dashboard/test_usecases/test_study_history.py b/e2e_tests/test_dashboard/test_usecases/test_study_history.py index 6c22b16b..f2d937bb 100644 --- a/e2e_tests/test_dashboard/test_usecases/test_study_history.py +++ b/e2e_tests/test_dashboard/test_usecases/test_study_history.py @@ -3,6 +3,7 @@ from playwright.sync_api import Page import pytest from ...test_server import make_test_server +from ...utils import clear_inmemory_cache def make_test_storage() -> optuna.storages.InMemoryStorage: @@ -23,6 +24,7 @@ def make_test_storage() -> optuna.storages.InMemoryStorage: @pytest.fixture def storage() -> optuna.storages.InMemoryStorage: + clear_inmemory_cache() storage = make_test_storage() return storage diff --git a/e2e_tests/test_dashboard/visual_regression_test.py b/e2e_tests/test_dashboard/visual_regression_test.py index e5c3a992..ab189343 100644 --- a/e2e_tests/test_dashboard/visual_regression_test.py +++ b/e2e_tests/test_dashboard/visual_regression_test.py @@ -1,19 +1,11 @@ from typing import Callable import optuna -from optuna_dashboard._storage import trials_cache -from optuna_dashboard._storage import trials_cache_lock -from optuna_dashboard._storage import trials_last_fetched_at from playwright.sync_api import Page import pytest from ..test_server import make_test_server - - -def clear_inmemory_cache() -> None: - with trials_cache_lock: - trials_cache.clear() - trials_last_fetched_at.clear() +from ..utils import clear_inmemory_cache @pytest.fixture diff --git a/e2e_tests/utils.py b/e2e_tests/utils.py new file mode 100644 index 00000000..68952ae9 --- /dev/null +++ b/e2e_tests/utils.py @@ -0,0 +1,9 @@ +from optuna_dashboard._storage import trials_cache +from optuna_dashboard._storage import trials_cache_lock +from optuna_dashboard._storage import trials_last_fetched_at + + +def clear_inmemory_cache() -> None: + with trials_cache_lock: + trials_cache.clear() + trials_last_fetched_at.clear() From e1f43e1ce4e139df05bf48b282d09ddffa060955 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 18:14:24 +0900 Subject: [PATCH 93/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index e1ab12c9..bb4c2bf8 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -1,5 +1,8 @@ +from __future__ import annotations + from typing import Any + import optuna from optuna.trial import TrialState from optuna_dashboard._app import create_app From a4fb8fdcdbf33393da21cf75ab2be9c0dd344e81 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 18:14:55 +0900 Subject: [PATCH 94/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index bb4c2bf8..3c615995 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -15,15 +15,23 @@ def _validate_output( storage: optuna.storages.BaseStorage, correct_status: int, study_id: int, + expect_no_result: bool = False, + extra_col_names: list[str] | None = None, ) -> None: app = create_app(storage) - status, _, _ = send_request( + status, _, body = send_request( app, f"/csv/{study_id}", "GET", content_type="application/json", ) assert status == correct_status + decoded_csv = str(body.decode("utf-8")) + if expect_no_result: + assert "is not found" in decoded_csv + else: + col_names = ["Number", "State"] + ([] if extra_col_names is None else extra_col_names) + assert all(col_name in decoded_csv for col_name in col_names) def test_download_csv_no_trial() -> None: From 1e5d582572b29b4fa85ed239a258de15df079581 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 18:15:06 +0900 Subject: [PATCH 95/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index 3c615995..a93789e7 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -71,7 +71,9 @@ def test_download_csv_fail(study_id: int) -> None: study = optuna.create_study(storage=storage) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) - _validate_output(storage, 404 if study_id != 0 else 200, study_id) + expect_no_result = study_id != 0 + cols = ["Param x", "Param y", "Value"] + _validate_output(storage, 404 if expect_no_result else 200, study_id, expect_no_result, cols) @pytest.mark.parametrize("is_multi_obj", [True, False]) From d7ff6f41f45fb1d8dbc5e61ff3642d7c99f52f5f Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 18:15:19 +0900 Subject: [PATCH 96/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index a93789e7..3eb0f330 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -90,7 +90,10 @@ def test_download_csv_multi_obj(is_multi_obj: bool) -> None: study = optuna.create_study(storage=storage, directions=directions) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) - _validate_output(storage, 200, 0) + cols = ["Param x", "Param y"] + cols += ["Objective 0", "Objective 1"] if is_multi_obj else ["Value"] + _validate_output(storage, 200, 0, extra_col_names=cols) + def test_download_csv_user_attr() -> None: From a8c451ada283cecf18aa8f8a036f7ea1816d5999 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 18:15:30 +0900 Subject: [PATCH 97/99] Update python_tests/test_csv_download.py Co-authored-by: Shuhei Watanabe <47781922+nabenabe0928@users.noreply.github.com> --- python_tests/test_csv_download.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index 3eb0f330..f43c8751 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -107,4 +107,6 @@ def test_download_csv_user_attr() -> None: study = optuna.create_study(storage=storage) optuna.logging.set_verbosity(optuna.logging.ERROR) study.optimize(objective, n_trials=10) - _validate_output(storage, 200, 0) + cols = ["Param x", "Param y", "Value", "UserAttribute abs_y"] + _validate_output(storage, 200, 0, extra_col_names=cols) + From f63b5e9cedd733df22cd6cab3da3731a4ffcbf58 Mon Sep 17 00:00:00 2001 From: Hiroki Takizawa Date: Thu, 7 Dec 2023 18:21:46 +0900 Subject: [PATCH 98/99] fix lint --- python_tests/test_csv_download.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python_tests/test_csv_download.py b/python_tests/test_csv_download.py index f43c8751..019f18ba 100644 --- a/python_tests/test_csv_download.py +++ b/python_tests/test_csv_download.py @@ -2,7 +2,6 @@ from __future__ import annotations from typing import Any - import optuna from optuna.trial import TrialState from optuna_dashboard._app import create_app @@ -95,7 +94,6 @@ def test_download_csv_multi_obj(is_multi_obj: bool) -> None: _validate_output(storage, 200, 0, extra_col_names=cols) - def test_download_csv_user_attr() -> None: def objective(trial: optuna.Trial) -> float: x = trial.suggest_float("x", -100, 100) @@ -109,4 +107,3 @@ def test_download_csv_user_attr() -> None: study.optimize(objective, n_trials=10) cols = ["Param x", "Param y", "Value", "UserAttribute abs_y"] _validate_output(storage, 200, 0, extra_col_names=cols) - From c38d0d9f81449ea659da8f892933e67eb9d14a44 Mon Sep 17 00:00:00 2001 From: c-bata Date: Fri, 8 Dec 2023 14:16:49 +0900 Subject: [PATCH 99/99] Bump the version up to v0.14.0 --- optuna_dashboard/__init__.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/optuna_dashboard/__init__.py b/optuna_dashboard/__init__.py index 8b793e62..2ca1d487 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.0" +__version__ = "0.14.0"