mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-23 13:30:25 +08:00
269 lines
7.8 KiB
TypeScript
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}
|
|
/>
|
|
)
|
|
}
|