diff --git a/optuna_dashboard/ts/components/TrialList.tsx b/optuna_dashboard/ts/components/TrialList.tsx index 63abade1..839129ed 100644 --- a/optuna_dashboard/ts/components/TrialList.tsx +++ b/optuna_dashboard/ts/components/TrialList.tsx @@ -5,9 +5,12 @@ import StopCircleIcon from "@mui/icons-material/StopCircle" import { Box, Button, + FormControl, IconButton, + InputLabel, Menu, MenuItem, + Select, Typography, useTheme, } from "@mui/material" @@ -24,11 +27,11 @@ import React, { FC, ReactNode, useMemo } from "react" import ListItemIcon from "@mui/material/ListItemIcon" import { useVirtualizer } from "@tanstack/react-virtual" import { useNavigate } from "react-router-dom" -import { useRecoilValue } from "recoil" +import { useRecoilState, useRecoilValue } from "recoil" import { FormWidgets, StudyDetail, Trial } from "ts/types/optuna" import { actionCreator } from "../action" import { useConstants } from "../constantsProvider" -import { artifactIsAvailable } from "../state" +import { artifactIsAvailable, trialListDurationTimeUnitState } from "../state" import { useQuery } from "../urlQuery" import { TrialArtifactCards } from "./Artifact/TrialArtifactCards" import { TrialNote } from "./Note" @@ -134,11 +137,34 @@ export const TrialListDetail: FC<{ const artifactEnabled = useRecoilValue(artifactIsAvailable) const startMs = trial.datetime_start?.getTime() const completeMs = trial.datetime_complete?.getTime() + const [durationTimeUnit, setDurationTimeUnit] = useRecoilState( + trialListDurationTimeUnitState + ) + const duration = useMemo( + () => + startMs !== undefined && completeMs !== undefined + ? (completeMs - startMs) / + 10 ** + (durationTimeUnit === "ms" + ? 0 + : durationTimeUnit === "s" + ? 3 + : durationTimeUnit === "min" + ? 6 + : 9) + : null, + [startMs, completeMs, durationTimeUnit] + ) const params = trial.state === "Waiting" ? trial.fixed_params : trial.params - const info: [string, string | null | ReactNode][] = [ - ["Value", trial.values?.map((v) => v.toString()).join(", ") || "None"], + const info: [string, string | ReactNode, string | null | ReactNode][] = [ [ + "value", + "Value", + trial.values?.map((v) => v.toString()).join(", ") || "None", + ], + [ + "intermediate_values", "Intermediate Values", {trial.intermediate_values.map((v) => ( @@ -149,6 +175,7 @@ export const TrialListDetail: FC<{ , ], [ + "parameter", "Parameter", {params.map((p) => ( @@ -159,20 +186,52 @@ export const TrialListDetail: FC<{ , ], [ + "started_at", "Started At", trial?.datetime_start ? trial?.datetime_start.toString() : null, ], [ + "completed_at", "Completed At", trial?.datetime_complete ? trial?.datetime_complete.toString() : null, ], [ - "Duration (ms)", - startMs !== undefined && completeMs !== undefined - ? (completeMs - startMs).toString() - : null, + "duration", + + + Duration + + + + Time Unit + + + + , + duration, ], [ + "user_attrs", "User Attributes", {trial.user_attrs.map((t) => ( @@ -185,6 +244,7 @@ export const TrialListDetail: FC<{ ] const renderInfo = ( key: string, + attribute: string | ReactNode, value: string | null | ReactNode ): ReactNode => ( - - {key} - + {typeof attribute === "string" ? ( + + {attribute} + + ) : ( + attribute + )} - {info.map(([key, value]) => - value !== null ? renderInfo(key, value) : null + {info.map(([key, attribute, value]) => + value !== null ? renderInfo(key, attribute, value) : null )} {artifactEnabled && } diff --git a/optuna_dashboard/ts/state.ts b/optuna_dashboard/ts/state.ts index aa6be637..1dc5ef33 100644 --- a/optuna_dashboard/ts/state.ts +++ b/optuna_dashboard/ts/state.ts @@ -66,6 +66,11 @@ export const studyDetailLoadingState = atom>({ default: {}, }) +export const trialListDurationTimeUnitState = atom<"ms" | "s" | "min" | "h">({ + key: "trialListDurationTimeUnit", + default: "ms", +}) + export const usePlotBackendRendering = () => { return useLocalStorage("plotBackendRendering", false) }