import React, { FC, useEffect, useState } from "react" import { useRecoilValue } from "recoil" import { Link, useParams } from "react-router-dom" import { createStyles, fade, makeStyles, Theme } from "@material-ui/core/styles" import { AppBar, Card, Typography, CardContent, Container, Grid, Toolbar, Paper, Box, IconButton, Select, MenuItem, } from "@material-ui/core" import { Home, Cached } from "@material-ui/icons" import { DataGridColumn, DataGrid } from "./DataGrid" import { GraphParallelCoordinate } from "./GraphParallelCoordinate" import { HyperparameterImportances } from "./HyperparameterImportances" import { Edf } from "./GraphEdf" import { GraphIntermediateValues } from "./GraphIntermediateValues" import { GraphSlice } from "./GraphSlice" import { GraphHistory } from "./GraphHistory" import { GraphParetoFront } from "./GraphParetoFront" 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), }, reload: { position: "relative", borderRadius: theme.shape.borderRadius, backgroundColor: fade(theme.palette.common.white, 0.15), "&:hover": { backgroundColor: fade(theme.palette.common.white, 0.25), }, marginLeft: 0, width: "100%", [theme.breakpoints.up("sm")]: { marginLeft: theme.spacing(1), width: "auto", }, }, reloadIcon: { padding: theme.spacing(0, 2), height: "100%", position: "absolute", pointerEvents: "none", display: "flex", alignItems: "center", justifyContent: "center", }, reloadSelect: { color: "inherit", padding: theme.spacing(1, 1, 1, 0), // vertical padding + font size from searchIcon paddingLeft: `calc(1em + ${theme.spacing(4)}px)`, transition: theme.transitions.create("width"), width: "100%", [theme.breakpoints.up("sm")]: { width: "14ch", "&:focus": { width: "20ch", }, }, }, 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(studyDetailsState) return studyDetails[studyId] || null } export const StudyDetail: FC = () => { const classes = useStyles() const action = actionCreator() const { studyId } = useParams() const studyIdNumber = parseInt(studyId, 10) const studyDetail = useStudyDetailValue(studyIdNumber) const [openReloadIntervalSelect, setOpenReloadIntervalSelect] = useState< boolean >(false) const [reloadInterval, setReloadInterval] = useState(10) useEffect(() => { action.updateStudyDetail(studyIdNumber) }, []) useEffect(() => { if (reloadInterval < 0) { return } const intervalId = setInterval(function () { action.updateStudyDetail(studyIdNumber) }, reloadInterval * 1000) return () => clearInterval(intervalId) }, [reloadInterval]) const title = studyDetail !== null ? studyDetail.name : `Study #${studyId}` const trials: Trial[] = studyDetail !== null ? studyDetail.trials : [] return (
{APP_BAR_TITLE}
{ setOpenReloadIntervalSelect(!openReloadIntervalSelect) }} >
{title} {studyDetail !== null && isSingleObjectiveStudy(studyDetail) ? ( ) : null} {studyDetail !== null && isSingleObjectiveStudy(studyDetail) ? ( ) : null} {studyDetail !== null ? ( ) : null} {studyDetail !== null && !isSingleObjectiveStudy(studyDetail) ? ( ) : null}
) } const TrialTable: FC<{ studyDetail: StudyDetail | null }> = ({ studyDetail, }) => { const trials: Trial[] = studyDetail !== null ? studyDetail.trials : [] const columns: DataGridColumn[] = [ { 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, less: (i, j): number => { const firstVal = trials[i].values?.[0] const secondVal = trials[j].values?.[0] if (firstVal === secondVal) { return 0 } else if (firstVal && secondVal) { return firstVal < secondVal ? 1 : -1 } else if (firstVal) { return -1 } else { return 1 } }, toCellValue: (i) => { if (trials[i].values === undefined) { return null } return trials[i].values?.[0] }, }) } else { const objectiveColumns: DataGridColumn< Trial >[] = studyDetail.directions.map((s, objectiveId) => ({ field: "values", label: `Objective ${objectiveId}`, sortable: true, less: (i, j): number => { const firstVal = trials[i].values?.[objectiveId] const secondVal = trials[j].values?.[objectiveId] if (firstVal === secondVal) { return 0 } else if (firstVal && secondVal) { return firstVal < secondVal ? 1 : -1 } else if (firstVal) { return -1 } else { return 1 } }, toCellValue: (i) => { if (trials[i].values === undefined) { return null } return trials[i].values?.[objectiveId] }, })) columns.push(...objectiveColumns) } columns.push({ field: "datetime_start", label: "Duration(ms)", toCellValue: (i) => { const startMs = trials[i].datetime_start?.getTime() const completeMs = trials[i].datetime_complete?.getTime() if (startMs !== undefined && completeMs !== undefined) { return (completeMs - startMs).toString() } return null }, sortable: true, less: (i, j): number => { const firstStartMs = trials[i].datetime_start?.getTime() const firstCompleteMs = trials[i].datetime_complete?.getTime() const firstDurationMs = firstStartMs !== undefined && firstCompleteMs !== undefined ? firstCompleteMs - firstStartMs : undefined const secondStartMs = trials[j].datetime_start?.getTime() const secondCompleteMs = trials[j].datetime_complete?.getTime() const secondDurationMs = secondStartMs !== undefined && secondCompleteMs !== undefined ? secondCompleteMs - secondStartMs : undefined if (firstDurationMs === secondDurationMs) { return 0 } else if ( firstDurationMs !== undefined && secondDurationMs !== undefined ) { return firstDurationMs < secondDurationMs ? 1 : -1 } else if (firstDurationMs !== undefined) { return -1 } else { return 1 } }, }) columns.push({ field: "params", label: "Params", toCellValue: (i) => trials[i].params.map((p) => p.name + ": " + p.value).join(", "), }) const collapseParamColumns: DataGridColumn[] = [ { 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[] = [ { field: "key", label: "Key", sortable: true }, { field: "value", label: "Value", sortable: true }, ] const collapseBody = (index: number) => { return ( Parameters columns={collapseParamColumns} rows={trials[index].params} keyField={"name"} dense={true} rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} /> Trial user attributes columns={collapseAttrColumns} rows={trials[index].user_attrs} keyField={"key"} dense={true} rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} /> Intermediate values columns={collapseIntermediateValueColumns} rows={trials[index].intermediate_values} keyField={"step"} dense={true} rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} /> Trial system attributes columns={collapseAttrColumns} rows={trials[index].system_attrs} keyField={"key"} dense={true} rowsPerPageOption={[5, 10, { label: "All", value: -1 }]} /> ) } return ( columns={columns} rows={trials} keyField={"trial_id"} dense={true} collapseBody={collapseBody} /> ) }