From c17e9127cab4ebb6e5c772cef505c8da54eac66c Mon Sep 17 00:00:00 2001 From: c-bata Date: Tue, 12 Jan 2021 21:35:00 +0900 Subject: [PATCH] Add dialog to create multi-objective study --- optuna_dashboard/app.py | 22 +- optuna_dashboard/static/action.ts | 4 +- optuna_dashboard/static/apiClient.ts | 4 +- .../static/components/StudyList.tsx | 200 ++++++++++++++++-- 4 files changed, 204 insertions(+), 26 deletions(-) diff --git a/optuna_dashboard/app.py b/optuna_dashboard/app.py index 811bb248..fdfe1039 100644 --- a/optuna_dashboard/app.py +++ b/optuna_dashboard/app.py @@ -130,8 +130,12 @@ def create_app(storage_or_url: Union[str, BaseStorage]) -> Bottle: response.content_type = "application/json" study_name = request.json.get("study_name", None) - direction = request.json.get("direction", None) - if study_name is None or direction not in ("minimize", "maximize"): + directions = request.json.get("directions", []) + if ( + study_name is None + or len(directions) == 0 + or not all([d in ("minimize", "maximize") for d in directions]) + ): response.status = 400 # Bad request return {"reason": "You need to set study_name and direction"} @@ -141,11 +145,15 @@ def create_app(storage_or_url: Union[str, BaseStorage]) -> Bottle: response.status = 400 # Bad request return {"reason": f"'{study_name}' is already exists"} - # TODO(c-bata): Support multi-objective study. - if direction.lower() == "maximize": - storage.set_study_directions(study_id, [StudyDirection.MAXIMIZE]) - else: - storage.set_study_directions(study_id, [StudyDirection.MINIMIZE]) + storage.set_study_directions( + study_id, + [ + StudyDirection.MAXIMIZE + if d.lower() == "maximize" + else StudyDirection.MINIMIZE + for d in directions + ], + ) summary = get_study_summary(storage, study_id) if summary is None: diff --git a/optuna_dashboard/static/action.ts b/optuna_dashboard/static/action.ts index 85741ab4..dd2b2d51 100644 --- a/optuna_dashboard/static/action.ts +++ b/optuna_dashboard/static/action.ts @@ -49,8 +49,8 @@ export const actionCreator = () => { }) } - const createNewStudy = (studyName: string, direction: StudyDirection) => { - createNewStudyAPI(studyName, direction) + const createNewStudy = (studyName: string, directions: StudyDirection[]) => { + createNewStudyAPI(studyName, directions) .then((study_summary) => { const newVal = [...studySummaries, study_summary] setStudySummaries(newVal) diff --git a/optuna_dashboard/static/apiClient.ts b/optuna_dashboard/static/apiClient.ts index e8867420..213e9984 100644 --- a/optuna_dashboard/static/apiClient.ts +++ b/optuna_dashboard/static/apiClient.ts @@ -138,12 +138,12 @@ interface CreateNewStudyResponse { export const createNewStudyAPI = ( studyName: string, - direction: StudyDirection + directions: StudyDirection[] ): Promise => { return axiosInstance .post(`/api/studies`, { study_name: studyName, - direction, + directions, }) .then((res) => { const study_summary = res.data.study_summary diff --git a/optuna_dashboard/static/components/StudyList.tsx b/optuna_dashboard/static/components/StudyList.tsx index d0fd809a..d88dd1f4 100644 --- a/optuna_dashboard/static/components/StudyList.tsx +++ b/optuna_dashboard/static/components/StudyList.tsx @@ -20,8 +20,13 @@ import { DialogActions, FormControlLabel, Checkbox, + Menu, + MenuItem, + FormControl, + FormLabel, + Select, } from "@material-ui/core" -import { AddBox, Delete, Refresh } from "@material-ui/icons" +import { Add, AddBox, Delete, Refresh, Remove } from "@material-ui/icons" import { actionCreator } from "../action" import { DataGrid, DataGridColumn } from "./DataGrid" @@ -35,18 +40,38 @@ const useStyles = makeStyles((theme: Theme) => grow: { flexGrow: 1, }, + objectiveButton: { + marginRight: theme.spacing(1), + }, }) ) export const StudyList: FC<{}> = () => { const classes = useStyles() - const [openNewStudyDialog, setOpenNewStudyDialog] = React.useState(false) + + const [ + newStudySelectionAnchorEl, + setNewStudySelectionAnchorEl, + ] = React.useState(null) + const openNewStudySelection = Boolean(newStudySelectionAnchorEl) + const [ + openNewSingleObjectiveStudyDialog, + setOpenNewSingleObjectiveStudyDialog, + ] = React.useState(false) + const [ + openNewMultiObjectiveStudyDialog, + setOpenNewMultiObjectiveStudyDialog, + ] = React.useState(false) const [openDeleteStudyDialog, setOpenDeleteStudyDialog] = React.useState( false ) const [deleteStudyID, setDeleteStudyID] = React.useState(-1) const [newStudyName, setNewStudyName] = React.useState("") - const [maximize, setMaximize] = React.useState(false) + + const [maximize, setMaximize] = React.useState(false) + const [directions, setDirections] = React.useState([ + "minimize", + ]) const action = actionCreator() const studies = useRecoilValue(studySummariesState) @@ -118,18 +143,31 @@ export const StudyList: FC<{}> = () => { { field: "value", label: "Value", sortable: true }, ] - const handleCloseNewStudyDialog = () => { + const handleCloseNewSingleObjectiveStudyDialog = () => { setNewStudyName("") - setOpenNewStudyDialog(false) + setOpenNewSingleObjectiveStudyDialog(false) } - const handleCreateNewStudy = () => { + const handleCreateNewSingleObjectiveStudy = () => { const direction = maximize ? "maximize" : "minimize" - action.createNewStudy(newStudyName, direction) - setOpenNewStudyDialog(false) + action.createNewStudy(newStudyName, [direction]) + setOpenNewSingleObjectiveStudyDialog(false) setNewStudyName("") } + const handleCloseNewMultiObjectiveStudyDialog = () => { + setOpenNewMultiObjectiveStudyDialog(false) + setNewStudyName("") + setDirections(["minimize"]) + } + + const handleCreateNewMultiObjectiveStudy = () => { + action.createNewStudy(newStudyName, directions) + setOpenNewMultiObjectiveStudyDialog(false) + setNewStudyName("") + setDirections(["minimize"]) + } + const handleCloseDeleteStudyDialog = () => { setOpenDeleteStudyDialog(false) setDeleteStudyID(-1) @@ -199,12 +237,46 @@ export const StudyList: FC<{}> = () => { aria-controls="menu-appbar" aria-haspopup="true" onClick={(e) => { - setOpenNewStudyDialog(true) + setNewStudySelectionAnchorEl(e.currentTarget) }} color="inherit" > + { + setNewStudySelectionAnchorEl(null) + }} + > + { + setNewStudySelectionAnchorEl(null) + setOpenNewSingleObjectiveStudyDialog(true) + }} + > + Single-objective + + { + setNewStudySelectionAnchorEl(null) + setOpenNewMultiObjectiveStudyDialog(true) + }} + > + Multi-objective + + @@ -221,13 +293,15 @@ export const StudyList: FC<{}> = () => { { - handleCloseNewStudyDialog() + handleCloseNewSingleObjectiveStudyDialog() }} - aria-labelledby="create-study-form-dialog-title" + aria-labelledby="create-single-objective-study-form-dialog-title" > - New study + + New single-objective study + To create a new study, please enter the study name here. @@ -259,11 +333,14 @@ export const StudyList: FC<{}> = () => { /> - + { + handleCloseNewMultiObjectiveStudyDialog() + }} + aria-labelledby="create-multi-objective-study-form-dialog-title" + > + + New multi-objective study + + + + To create a new study, please enter the study name and directions + here. + + { + setNewStudyName(e.target.value) + }} + fullWidth + /> + + {directions.map((d, i) => ( + + + Objective {i}: + + + + ))} + + + + + + + + + {