Files
optuna-dashboard/optuna_dashboard/static/components/StudyDetail.tsx
T

269 lines
7.8 KiB
TypeScript

import React, { FC, useEffect, useState } from "react"
import { useRecoilValue } from "recoil"
import { Link, useParams } from "react-router-dom"
import { createStyles, makeStyles, Theme } from "@material-ui/core/styles"
import {
AppBar,
Card,
Typography,
CardContent,
Container,
Grid,
Toolbar,
Paper,
Box,
IconButton,
FormControl,
FormLabel,
Switch,
} from "@material-ui/core"
import { Home } from "@material-ui/icons"
import { DataGridColumn, DataGrid } from "./DataGrid"
import { GraphParallelCoordinate } from "./GraphParallelCoordinate"
import { GraphIntermediateValues } from "./GraphIntermediateValues"
import { GraphHistory } from "./GraphHistory"
import { actionCreator } from "../action"
import { studyDetailsState } from "../state"
const useStyles = makeStyles((theme: Theme) =>
createStyles({
paper: {
margin: theme.spacing(2),
padding: theme.spacing(2),
},
card: {
margin: theme.spacing(2),
},
grow: {
flexGrow: 1,
},
})
)
interface ParamTypes {
studyId: string
}
const isSingleObjectiveStudy = (studyDetail: StudyDetail): boolean => {
return studyDetail.directions.length === 1
}
export const useStudyDetailValue = (studyId: number): StudyDetail | null => {
const studyDetails = useRecoilValue<StudyDetails>(studyDetailsState)
return studyDetails[studyId] || null
}
export const StudyDetail: FC<{}> = () => {
const classes = useStyles()
const action = actionCreator()
const { studyId } = useParams<ParamTypes>()
const studyIdNumber = parseInt(studyId, 10)
const studyDetail = useStudyDetailValue(studyIdNumber)
const [autoReload, setAutoReload] = useState<boolean>(false)
useEffect(() => {
action.updateStudyDetail(studyIdNumber)
}, [])
useEffect(() => {
if (!autoReload) {
return
}
const intervalId = setInterval(function () {
action.updateStudyDetail(studyIdNumber)
}, 10 * 1000)
return () => clearInterval(intervalId)
}, [autoReload])
const title = studyDetail !== null ? studyDetail.name : `Study #${studyId}`
const trials: Trial[] = studyDetail !== null ? studyDetail.trials : []
return (
<div>
<AppBar position="static">
<Container>
<Toolbar>
<Typography variant="h6">{APP_BAR_TITLE}</Typography>
<div className={classes.grow} />
<IconButton
aria-controls="menu-appbar"
aria-haspopup="true"
component={Link}
to={URL_PREFIX + "/"}
color="inherit"
>
<Home />
</IconButton>
</Toolbar>
</Container>
</AppBar>
<Container>
<div>
<Paper className={classes.paper}>
<Typography variant="h6">{title}</Typography>
<FormControl component="fieldset">
<FormLabel component="legend">Auto-reload:</FormLabel>
<Switch
checked={autoReload}
onChange={(e) => {
setAutoReload(!autoReload)
}}
value="enable"
/>
</FormControl>
</Paper>
<Card className={classes.card}>
<CardContent>
<GraphHistory study={studyDetail} />
</CardContent>
</Card>
{studyDetail !== null && isSingleObjectiveStudy(studyDetail) ? (
<Grid container direction="row">
<Grid item xs={6}>
<Card className={classes.card}>
<CardContent>
<GraphParallelCoordinate trials={trials} />
</CardContent>
</Card>
</Grid>
<Grid item xs={6}>
<Card className={classes.card}>
<CardContent>
<GraphIntermediateValues trials={trials} />
</CardContent>
</Card>
</Grid>
</Grid>
) : null}
<Card className={classes.card}>
<TrialTable studyDetail={studyDetail} />
</Card>
</div>
</Container>
</div>
)
}
const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({
studyDetail,
}) => {
const trials: Trial[] = studyDetail !== null ? studyDetail.trials : []
let columns: DataGridColumn<Trial>[] = [
{ field: "number", label: "Number", sortable: true, padding: "none" },
{
field: "state",
label: "State",
sortable: true,
filterable: true,
padding: "none",
toCellValue: (i) => trials[i].state.toString(),
},
]
if (studyDetail === null || isSingleObjectiveStudy(studyDetail)) {
columns.push({
field: "values",
label: "Value",
sortable: true,
toCellValue: (i) => trials[i].values?.[0] || null,
})
} else {
const objectiveColumns: DataGridColumn<
Trial
>[] = studyDetail.directions.map((s, objectiveId) => ({
field: "values",
label: `Objective ${objectiveId}`,
sortable: true,
toCellValue: (i) => trials[i].values?.[objectiveId] || null,
}))
columns.push(...objectiveColumns)
}
columns.push({
field: "params",
label: "Params",
toCellValue: (i) =>
trials[i].params.map((p) => p.name + ": " + p.value).join(", "),
})
const collapseParamColumns: DataGridColumn<TrialParam>[] = [
{ field: "name", label: "Name", sortable: true },
{ field: "value", label: "Value", sortable: true },
]
const collapseIntermediateValueColumns: DataGridColumn<
TrialIntermediateValue
>[] = [
{ field: "step", label: "Step", sortable: true },
{ field: "value", label: "Value", sortable: true },
]
const collapseAttrColumns: DataGridColumn<Attribute>[] = [
{ field: "key", label: "Key", sortable: true },
{ field: "value", label: "Value", sortable: true },
]
const collapseBody = (index: number) => {
return (
<Grid container direction="row">
<Grid item xs={6}>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
Parameters
</Typography>
<DataGrid<TrialParam>
columns={collapseParamColumns}
rows={trials[index].params}
keyField={"name"}
dense={true}
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
/>
<Typography variant="h6" gutterBottom component="div">
Trial user attributes
</Typography>
<DataGrid<Attribute>
columns={collapseAttrColumns}
rows={trials[index].user_attrs}
keyField={"key"}
dense={true}
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
/>
</Box>
</Grid>
<Grid item xs={6}>
<Box margin={1}>
<Typography variant="h6" gutterBottom component="div">
Intermediate values
</Typography>
<DataGrid<TrialIntermediateValue>
columns={collapseIntermediateValueColumns}
rows={trials[index].intermediate_values}
keyField={"step"}
dense={true}
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
/>
<Typography variant="h6" gutterBottom component="div">
Trial system attributes
</Typography>
<DataGrid<Attribute>
columns={collapseAttrColumns}
rows={trials[index].system_attrs}
keyField={"key"}
dense={true}
rowsPerPageOption={[5, 10, { label: "All", value: -1 }]}
/>
</Box>
</Grid>
</Grid>
)
}
return (
<DataGrid<Trial>
columns={columns}
rows={trials}
keyField={"trial_id"}
dense={true}
collapseBody={collapseBody}
/>
)
}