diff --git a/optuna_dashboard/ts/apiClient.ts b/optuna_dashboard/ts/apiClient.ts
index a539c590..d7812a56 100644
--- a/optuna_dashboard/ts/apiClient.ts
+++ b/optuna_dashboard/ts/apiClient.ts
@@ -40,8 +40,8 @@ interface StudyDetailResponse {
name: string
datetime_start: string
directions: StudyDirection[]
- best_trial?: TrialResponse
trials: TrialResponse[]
+ best_trials: TrialResponse[]
intersection_search_space: SearchSpace[]
union_search_space: SearchSpace[]
union_user_attrs: AttributeSpec[]
@@ -66,12 +66,16 @@ export const getStudyDetailAPI = (
const trials = res.data.trials.map((trial): Trial => {
return convertTrialResponse(trial)
})
+ const best_trials = res.data.best_trials.map((trial): Trial => {
+ return convertTrialResponse(trial)
+ })
return {
id: studyId,
name: res.data.name,
datetime_start: new Date(res.data.datetime_start),
directions: res.data.directions,
trials: trials,
+ best_trials: best_trials,
union_search_space: res.data.union_search_space,
intersection_search_space: res.data.intersection_search_space,
union_user_attrs: res.data.union_user_attrs,
diff --git a/optuna_dashboard/ts/components/App.tsx b/optuna_dashboard/ts/components/App.tsx
index 756518ed..d149615b 100644
--- a/optuna_dashboard/ts/components/App.tsx
+++ b/optuna_dashboard/ts/components/App.tsx
@@ -2,6 +2,8 @@ import React, { FC, useMemo, useState, useEffect } from "react"
import { RecoilRoot } from "recoil"
import { BrowserRouter as Router, Switch, Route } from "react-router-dom"
import { SnackbarProvider } from "notistack"
+import blue from "@mui/material/colors/blue"
+import pink from "@mui/material/colors/pink"
import {
createTheme,
useMediaQuery,
@@ -25,6 +27,8 @@ export const App: FC = () => {
createTheme({
palette: {
mode: colorMode,
+ primary: blue,
+ secondary: pink,
},
}),
[colorMode]
@@ -53,7 +57,16 @@ export const App: FC = () => {
children={
+ }
+ />
+
}
/>
diff --git a/optuna_dashboard/ts/components/Note.tsx b/optuna_dashboard/ts/components/Note.tsx
index 51c9a4cf..16422395 100644
--- a/optuna_dashboard/ts/components/Note.tsx
+++ b/optuna_dashboard/ts/components/Note.tsx
@@ -16,7 +16,8 @@ import { actionCreator } from "../action"
export const Note: FC<{
studyId: number
latestNote: Note
-}> = ({ studyId, latestNote }) => {
+ minRows: number
+}> = ({ studyId, latestNote, minRows }) => {
const theme = useTheme()
const [saving, setSaving] = useState(false)
const [edited, setEdited] = useState(false)
@@ -72,7 +73,7 @@ export const Note: FC<{
{studyDetail !== null ? (
-
+
) : null}
diff --git a/optuna_dashboard/ts/components/StudyDetailBeta.tsx b/optuna_dashboard/ts/components/StudyDetailBeta.tsx
index 83afdbd2..35823bfc 100644
--- a/optuna_dashboard/ts/components/StudyDetailBeta.tsx
+++ b/optuna_dashboard/ts/components/StudyDetailBeta.tsx
@@ -1,47 +1,50 @@
import React, { FC, useEffect } from "react"
-import { useRecoilState, useRecoilValue } from "recoil"
-import { Link, useParams } from "react-router-dom"
-import MuiDrawer from "@mui/material/Drawer"
-import IconButton from "@mui/material/IconButton"
-import ChevronLeftIcon from "@mui/icons-material/ChevronLeft"
-import Divider from "@mui/material/Divider"
-import List from "@mui/material/List"
-import ListItem from "@mui/material/ListItem"
-import ListItemButton from "@mui/material/ListItemButton"
-import ListItemIcon from "@mui/material/ListItemIcon"
-import ListItemText from "@mui/material/ListItemText"
-import ListSubheader from "@mui/material/ListSubheader"
+import { useRecoilValue } from "recoil"
+import { useParams } from "react-router-dom"
import {
Card,
CardContent,
Box,
- useTheme,
- Switch,
- Theme,
- CSSObject,
- styled,
Typography,
- Toolbar,
+ useTheme,
+ ListItem,
} from "@mui/material"
+import Grid2 from "@mui/material/Unstable_Grid2"
import { GraphHistory } from "./GraphHistory"
import { Note } from "./Note"
import { actionCreator } from "../action"
-import { reloadIntervalState, studyDetailsState } from "../state"
+import {
+ reloadIntervalState,
+ studyDetailsState,
+ studySummariesState,
+} from "../state"
import { TrialTable } from "./TrialTable"
import { StudyDetailDrawer } from "./StudyDetailDrawer"
+import { GraphParallelCoordinate } from "./GraphParallelCoordinate"
+import { Contour } from "./GraphContour"
+import { GraphHyperparameterImportances } from "./GraphHyperparameterImportances"
+import { GraphSlice } from "./GraphSlice"
+import { GraphParetoFront } from "./GraphParetoFront"
+import { DataGrid, DataGridColumn } from "./DataGrid"
+import List from "@mui/material/List"
interface ParamTypes {
studyId: string
}
-type PageId = "top" | "trials" | "note"
+type PageId = "history" | "analytics" | "trials" | "note"
const useStudyDetailValue = (studyId: number): StudyDetail | null => {
const studyDetails = useRecoilValue(studyDetailsState)
return studyDetails[studyId] || null
}
+const useStudySummaryValue = (studyId: number): StudySummary | null => {
+ const studySummaries = useRecoilValue(studySummariesState)
+ return studySummaries.find((s) => s.study_id == studyId) || null
+}
+
export const StudyDetailBeta: FC<{
toggleColorMode: () => void
page: PageId
@@ -52,71 +55,169 @@ export const StudyDetailBeta: FC<{
const studyIdNumber = parseInt(studyId, 10)
const studyDetail = useStudyDetailValue(studyIdNumber)
const reloadInterval = useRecoilValue(reloadIntervalState)
+ const studySummary = useStudySummaryValue(studyIdNumber)
+ const directions = studyDetail?.directions || studySummary?.directions || null
+ const userAttrs = studySummary?.user_attrs || []
+
+ const title =
+ studyDetail !== null || studySummary !== null
+ ? `${studyDetail?.name || studySummary?.study_name} (id=${studyId})`
+ : `Study #${studyId}`
useEffect(() => {
action.updateStudyDetail(studyIdNumber)
}, [])
useEffect(() => {
- if (reloadInterval < 0) {
+ if (reloadInterval < 0 || page === "trials") {
return
}
const intervalId = setInterval(function () {
action.updateStudyDetail(studyIdNumber)
}, reloadInterval * 1000)
return () => clearInterval(intervalId)
- }, [reloadInterval, studyDetail])
+ }, [reloadInterval, studyDetail, page])
- const trials: Trial[] = studyDetail !== null ? studyDetail.trials : []
-
- const trialListWidth = 240
+ const userAttrColumns: DataGridColumn[] = [
+ { field: "key", label: "Key", sortable: true },
+ { field: "value", label: "Value", sortable: true },
+ ]
let content = null
- if (page === "top") {
+ if (page === "history") {
content = (
-
-
-
- {`Trials (${
- studyDetail?.trials.length || 0
- })`}
- {trials.map((trial, i) => {
- return (
-
-
-
-
-
- )
- })}
-
-
-
-
+
-
+
+
+
+
+ {directions !== null && directions.length > 1 ? (
+
-
+
-
+ ) : null}
+
+
+
+
+ {studyDetail !== null &&
+ studyDetail.best_trials.length === 1 && (
+ <>
+
+ Best Trial
+
+
+ {studyDetail.best_trials[0].values}
+
+
+ {studyDetail.best_trials[0].params.map((param) => (
+
+ {param.name} {param.value}
+
+ ))}
+
+ >
+ )}
+ {studyDetail !== null && studyDetail.best_trials.length > 1 && (
+ <>
+
+ Best Trials
+
+ >
+ )}
+
+
+
+
+
+
+
+ Study User Attributes
+
+
+ columns={userAttrColumns}
+ rows={userAttrs}
+ keyField={"key"}
+ dense={true}
+ initialRowsPerPage={5}
+ rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
+ />
+
+
+
+
+
+ )
+ } else if (page === "analytics") {
+ content = (
+
+
+ Hyperparameter Importance
+
+
+
+
+
+
+
+ Hyperparameter Relationships
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
)
} else if (page === "trials") {
- content =
+ content = (
+
+
+
+
+
+ )
} else {
content =
studyDetail !== null ? (
-
+
) : null
}
@@ -126,6 +227,7 @@ export const StudyDetailBeta: FC<{
studyId={studyIdNumber}
page={page}
toggleColorMode={toggleColorMode}
+ title={title}
>
{content}
diff --git a/optuna_dashboard/ts/components/StudyDetailDrawer.tsx b/optuna_dashboard/ts/components/StudyDetailDrawer.tsx
index 7c8e67c7..be9e9d6e 100644
--- a/optuna_dashboard/ts/components/StudyDetailDrawer.tsx
+++ b/optuna_dashboard/ts/components/StudyDetailDrawer.tsx
@@ -27,8 +27,9 @@ import TableViewIcon from "@mui/icons-material/TableView"
import RateReviewIcon from "@mui/icons-material/RateReview"
import ClearIcon from "@mui/icons-material/Clear"
import MenuIcon from "@mui/icons-material/Menu"
-import GitHubIcon from '@mui/icons-material/GitHub';
-import OpenInNewIcon from '@mui/icons-material/OpenInNew';
+import GitHubIcon from "@mui/icons-material/GitHub"
+import OpenInNewIcon from "@mui/icons-material/OpenInNew"
+import QueryStatsIcon from "@mui/icons-material/QueryStats"
import { Switch } from "@mui/material"
const drawerWidth = 240
@@ -105,11 +106,12 @@ const Drawer = styled(MuiDrawer, {
export const StudyDetailDrawer: FC<{
studyId: number
toggleColorMode: () => void
- page: "top" | "trials" | "note"
+ page: "history" | "analytics" | "trials" | "note"
+ title: string
children?: React.ReactNode
-}> = ({ studyId, toggleColorMode, page, children }) => {
+}> = ({ studyId, toggleColorMode, page, title, children }) => {
const theme = useTheme()
- const [open, setOpen] = React.useState(false)
+ const [open, setOpen] = React.useState(true)
const [reloadInterval, updateReloadInterval] =
useRecoilState(reloadIntervalState)
@@ -157,8 +159,8 @@ export const StudyDetailDrawer: FC<{
>
-
- Mini variant drawer
+
+ {title}
@@ -174,12 +176,12 @@ export const StudyDetailDrawer: FC<{
-
+
@@ -187,6 +189,19 @@ export const StudyDetailDrawer: FC<{
+
+
+
+
+
+
+
+
-
+
diff --git a/optuna_dashboard/ts/components/StudyList.tsx b/optuna_dashboard/ts/components/StudyList.tsx
index d0c9218f..1a57d4da 100644
--- a/optuna_dashboard/ts/components/StudyList.tsx
+++ b/optuna_dashboard/ts/components/StudyList.tsx
@@ -376,6 +376,7 @@ export const StudyList: FC<{
},
}}
/>
+
{sortBySelect}
diff --git a/optuna_dashboard/ts/types/index.d.ts b/optuna_dashboard/ts/types/index.d.ts
index 6541a93b..1cb85277 100644
--- a/optuna_dashboard/ts/types/index.d.ts
+++ b/optuna_dashboard/ts/types/index.d.ts
@@ -96,7 +96,7 @@ declare interface StudyDetail {
name: string
directions: StudyDirection[]
datetime_start: Date
- best_trial?: Trial
+ best_trials: Trial[]
trials: Trial[]
intersection_search_space: SearchSpace[]
union_search_space: SearchSpace[]