Merge pull request #863 from porink0424/fix/type-replace

Align the types defined in `optuna_dashboard/ts` to `@optuna/types` as much as possible
This commit is contained in:
c-bata
2024-04-17 17:06:46 +09:00
committed by GitHub
54 changed files with 576 additions and 1271 deletions
+23
View File
@@ -23,9 +23,32 @@ jobs:
uses: actions/setup-node@v2
with:
node-version: '20'
- run: npm install
- run: npm run lint
type-check:
name: Type checking on optuna-dashboard
runs-on: ubuntu-latest
steps:
- name: Check out code
uses: actions/checkout@master
- name: Setup Node
uses: actions/setup-node@v2
with:
node-version: '20'
- name: Setup tslib
run: make tslib
- name: Type Check
working-directory: optuna_dashboard
run: |
npm install
npm run type-check
build:
name: JS build check
runs-on: ubuntu-latest
+2 -1
View File
@@ -15,7 +15,8 @@
"ignore": [
"optuna_dashboard/ts/components/PlotlyColorTemplates.ts",
"tslib/react/src/components/PlotlyDarkMode.ts",
"tslib/**/pkg/*"
"tslib/**/pkg/*",
"tslib/react/types/*"
]
},
"javascript": {
+1 -1
View File
@@ -62,7 +62,7 @@ if TYPE_CHECKING:
"type": Literal["FloatDistribution"],
"low": float,
"high": float,
"step": float,
"step": Union[float, None],
"log": bool,
},
)
+11 -873
View File
File diff suppressed because it is too large Load Diff
+3 -1
View File
@@ -8,7 +8,8 @@
"watch": "NODE_ENV=development TYPESCRIPT_LOADER=esbuild-loader webpack --watch",
"build": "webpack",
"build:dev": "NODE_ENV=development TYPESCRIPT_LOADER=esbuild-loader webpack",
"build:prd": "NODE_ENV=production webpack"
"build:prd": "NODE_ENV=production webpack",
"type-check": "tsc --noEmit"
},
"author": "Masashi Shibata",
"license": "MIT",
@@ -45,6 +46,7 @@
"devDependencies": {
"@babel/core": "^7.23.9",
"@babel/preset-env": "^7.23.9",
"@optuna/types": "../tslib/types/",
"@types/plotly.js": "^2.12.32",
"@types/react": "^18.2.48",
"@types/react-dom": "^18.2.18",
+16 -3
View File
@@ -1,3 +1,4 @@
import * as Optuna from "@optuna/types"
import { useRecoilState, useSetRecoilState } from "recoil"
import { useSnackbar } from "notistack"
import {
@@ -33,6 +34,15 @@ import {
studySummariesLoadingState,
} from "./state"
import { getDominatedTrials } from "./dominatedTrials"
import {
Artifact,
FeedbackComponentType,
Note,
StudyDetail,
StudyDetails,
StudySummary,
Trial,
} from "./types/optuna"
// eslint-disable-next-line @typescript-eslint/explicit-module-boundary-types
export const actionCreator = () => {
@@ -143,8 +153,8 @@ export const actionCreator = () => {
const setTrialStateValues = (
studyId: number,
index: number,
state: TrialState,
values?: TrialValueNumber[]
state: Optuna.TrialState,
values?: number[]
) => {
const newTrial: Trial = Object.assign(
{},
@@ -270,7 +280,10 @@ export const actionCreator = () => {
})
}
const createNewStudy = (studyName: string, directions: StudyDirection[]) => {
const createNewStudy = (
studyName: string,
directions: Optuna.StudyDirection[]
) => {
createNewStudyAPI(studyName, directions)
.then((study_summary) => {
const newVal = [...studySummaries, study_summary]
+32 -16
View File
@@ -1,5 +1,21 @@
import * as plotly from "plotly.js-dist-min"
import axios from "axios"
import * as Optuna from "@optuna/types"
import {
Artifact,
FeedbackComponentType,
FormWidgets,
Note,
ParamImportance,
PlotlyGraphObject,
PreferenceFeedbackMode,
PreferenceHistory,
SearchSpaceItem,
StudyDetail,
StudySummary,
Trial,
TrialParam,
} from "./types/optuna"
const axiosInstance = axios.create({ baseURL: API_ENDPOINT })
@@ -18,9 +34,9 @@ interface TrialResponse {
trial_id: number
study_id: number
number: number
state: TrialState
values?: TrialValueNumber[]
intermediate_values: TrialIntermediateValue[]
state: Optuna.TrialState
values?: number[]
intermediate_values: Optuna.TrialIntermediateValue[]
datetime_start?: string
datetime_complete?: string
params: TrialParam[]
@@ -28,7 +44,7 @@ interface TrialResponse {
name: string
param_external_value: string
}[]
user_attrs: Attribute[]
user_attrs: Optuna.Attribute[]
note: Note
artifacts: Artifact[]
constraints: number[]
@@ -86,13 +102,13 @@ const convertPreferenceHistory = (
interface StudyDetailResponse {
name: string
datetime_start: string
directions: StudyDirection[]
user_attrs: Attribute[]
directions: Optuna.StudyDirection[]
user_attrs: Optuna.Attribute[]
trials: TrialResponse[]
best_trials: TrialResponse[]
intersection_search_space: SearchSpaceItem[]
union_search_space: SearchSpaceItem[]
union_user_attrs: AttributeSpec[]
union_user_attrs: Optuna.AttributeSpec[]
has_intermediate_values: boolean
note: Note
is_preferential: boolean
@@ -155,8 +171,8 @@ interface StudySummariesResponse {
study_summaries: {
study_id: number
study_name: string
directions: StudyDirection[]
user_attrs: Attribute[]
directions: Optuna.StudyDirection[]
user_attrs: Optuna.Attribute[]
is_preferential: boolean
datetime_start?: string
}[]
@@ -185,8 +201,8 @@ interface CreateNewStudyResponse {
study_summary: {
study_id: number
study_name: string
directions: StudyDirection[]
user_attrs: Attribute[]
directions: Optuna.StudyDirection[]
user_attrs: Optuna.Attribute[]
is_preferential: boolean
datetime_start?: string
}
@@ -194,7 +210,7 @@ interface CreateNewStudyResponse {
export const createNewStudyAPI = (
studyName: string,
directions: StudyDirection[]
directions: Optuna.StudyDirection[]
): Promise<StudySummary> => {
return axiosInstance
.post<CreateNewStudyResponse>(`/api/studies`, {
@@ -226,8 +242,8 @@ export const deleteStudyAPI = (studyId: number): Promise<void> => {
type RenameStudyResponse = {
study_id: number
study_name: string
directions: StudyDirection[]
user_attrs: Attribute[]
directions: Optuna.StudyDirection[]
user_attrs: Optuna.Attribute[]
is_prefential: boolean
datetime_start?: string
}
@@ -338,10 +354,10 @@ export const deleteStudyArtifactAPI = (
export const tellTrialAPI = (
trialId: number,
state: TrialStateFinished,
state: Optuna.TrialStateFinished,
values?: number[]
): Promise<void> => {
const req: { state: TrialState; values?: number[] } = {
const req: { state: Optuna.TrialState; values?: number[] } = {
state: state,
values: values,
}
+1
View File
@@ -55,6 +55,7 @@ export const App: FC = () => {
<ThemeProvider theme={theme}>
<CssBaseline />
<Box
component="div"
sx={{
backgroundColor: colorMode === "dark" ? "#121212" : "#ffffff",
width: "100%",
+3 -2
View File
@@ -175,7 +175,7 @@ export const AppDrawer: FC<{
}
return (
<Box sx={{ display: "flex", width: "100%" }}>
<Box component="div" sx={{ display: "flex", width: "100%" }}>
<AppBar position="fixed" open={open}>
<Toolbar>
<IconButton
@@ -316,7 +316,7 @@ export const AppDrawer: FC<{
</ListItem>
</List>
)}
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
<Divider />
<List>
{studyId !== undefined && (
@@ -359,6 +359,7 @@ export const AppDrawer: FC<{
aria-describedby="modal-modal-description"
>
<Box
component="div"
sx={{
position: "absolute",
top: "50%",
@@ -6,6 +6,7 @@ import {
import { WaveSurferArtifactViewer } from "./WaveSurferArtifactViewer"
import InsertDriveFileIcon from "@mui/icons-material/InsertDriveFile"
import { CardMedia, Box } from "@mui/material"
import { Artifact } from "ts/types/optuna"
export const ArtifactCardMedia: FC<{
artifact: Artifact
@@ -37,6 +38,7 @@ export const ArtifactCardMedia: FC<{
} else if (artifact.mimetype.startsWith("audio")) {
return (
<Box
component="div"
style={{
width: "100%",
height: height,
@@ -8,6 +8,7 @@ import {
DialogActions,
} from "@mui/material"
import { actionCreator } from "../../action"
import { Artifact } from "ts/types/optuna"
export const useDeleteTrialArtifactDialog = (): [
(studyId: number, trialId: number, artifact: Artifact) => void,
@@ -27,6 +27,7 @@ import {
isThreejsArtifact,
} from "./ThreejsArtifactViewer"
import { ArtifactCardMedia } from "./ArtifactCardMedia"
import { StudyDetail } from "ts/types/optuna"
export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => {
const theme = useTheme()
@@ -40,7 +41,10 @@ export const StudyArtifactCards: FC<{ study: StudyDetail }> = ({ study }) => {
return (
<>
<Box sx={{ display: "flex", flexWrap: "wrap", p: theme.spacing(1, 0) }}>
<Box
component="div"
sx={{ display: "flex", flexWrap: "wrap", p: theme.spacing(1, 0) }}
>
{study.artifacts.map((artifact) => {
const urlPath = `/artifacts/${study.id}/${artifact.artifact_id}`
return (
@@ -9,6 +9,7 @@ import { PerspectiveCamera } from "three"
import { Modal, Box, useTheme } from "@mui/material"
import ClearIcon from "@mui/icons-material/Clear"
import IconButton from "@mui/material/IconButton"
import { Artifact } from "ts/types/optuna"
export const isThreejsArtifact = (artifact: Artifact): boolean => {
return (
@@ -145,6 +146,7 @@ export const useThreejsArtifactModal = (): [
}}
>
<Box
component="div"
sx={{
position: "absolute",
top: "50%",
@@ -27,6 +27,7 @@ import {
isThreejsArtifact,
} from "./ThreejsArtifactViewer"
import { ArtifactCardMedia } from "./ArtifactCardMedia"
import { Trial } from "ts/types/optuna"
export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => {
const theme = useTheme()
@@ -49,7 +50,10 @@ export const TrialArtifactCards: FC<{ trial: Trial }> = ({ trial }) => {
>
Artifacts
</Typography>
<Box sx={{ display: "flex", flexWrap: "wrap", p: theme.spacing(1, 0) }}>
<Box
component="div"
sx={{ display: "flex", flexWrap: "wrap", p: theme.spacing(1, 0) }}
>
{trial.artifacts.map((artifact) => {
const urlPath = `/artifacts/${trial.study_id}/${trial.trial_id}/${artifact.artifact_id}`
return (
@@ -14,6 +14,7 @@ import {
} from "@mui/material"
import { Link } from "react-router-dom"
import LinkIcon from "@mui/icons-material/Link"
import { StudyDetail, Trial } from "ts/types/optuna"
const useBestTrials = (studyDetail: StudyDetail | null): Trial[] => {
return useMemo(() => studyDetail?.best_trials || [], [studyDetail])
@@ -75,6 +76,7 @@ export const BestTrialsCard: FC<{
orientation="horizontal"
/>
<Box
component="div"
sx={{
overflow: "auto",
height: "450px",
@@ -1,4 +1,5 @@
import React, { FC, useEffect, useMemo, useState } from "react"
import * as Optuna from "@optuna/types"
import { useRecoilValue } from "recoil"
import { useSnackbar } from "notistack"
import { Link } from "react-router-dom"
@@ -31,6 +32,7 @@ import { GraphEdf } from "./GraphEdf"
import { GraphHistory } from "./GraphHistory"
import { useNavigate } from "react-router-dom"
import { useQuery } from "../urlQuery"
import { StudyDetails, StudySummary } from "ts/types/optuna"
const useQueriedStudies = (
studies: StudySummary[],
@@ -58,8 +60,8 @@ const getStudyListLink = (ids: number[]): string => {
}
const isEqualDirections = (
array1: StudyDirection[],
array2: StudyDirection[]
array1: Optuna.StudyDirection[],
array2: Optuna.StudyDirection[]
): boolean => {
let i = array1.length
if (i !== array2.length) return false
@@ -115,10 +117,14 @@ export const CompareStudies: FC<{
)
return (
<Box sx={{ display: "flex" }}>
<Box component="div" sx={{ display: "flex" }}>
<AppDrawer toggleColorMode={toggleColorMode} toolbar={toolbar}>
<Box sx={{ display: "flex", flexDirection: "row", width: "100%" }}>
<Box
component="div"
sx={{ display: "flex", flexDirection: "row", width: "100%" }}
>
<Box
component="div"
sx={{
minWidth: studyListWidth,
overflow: "auto",
@@ -130,7 +136,7 @@ export const CompareStudies: FC<{
<Typography sx={{ p: theme.spacing(1, 0) }}>
Compare studies with Shift+Click
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
</ListSubheader>
<Divider />
{studies.map((study) => {
@@ -200,6 +206,7 @@ export const CompareStudies: FC<{
primary={`${study.study_id}. ${study.study_name}`}
/>
<Box
component="div"
sx={{
display: "flex",
flexDirection: "row",
@@ -234,13 +241,17 @@ export const CompareStudies: FC<{
</Box>
<Divider orientation="vertical" flexItem />
<Box
component="div"
sx={{
flexGrow: 1,
overflow: "auto",
height: `calc(100vh - ${theme.spacing(8)})`,
}}
>
<Box sx={{ display: "flex", flexDirection: "row", width: "100%" }}>
<Box
component="div"
sx={{ display: "flex", flexDirection: "row", width: "100%" }}
>
<StudiesGraph studies={selected} />
</Box>
</Box>
@@ -274,7 +285,10 @@ const StudiesGraph: FC<{ studies: StudySummary[] }> = ({ studies }) => {
const showStudyDetails = studies.map((study) => studyDetails[study.study_id])
return (
<Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}>
<Box
component="div"
sx={{ display: "flex", width: "100%", flexDirection: "column" }}
>
<FormControl
component="fieldset"
sx={{
@@ -18,6 +18,8 @@ import { useRecoilValue } from "recoil"
import { studySummariesState } from "../state"
import RemoveIcon from "@mui/icons-material/Remove"
import AddIcon from "@mui/icons-material/Add"
import * as Optuna from "@optuna/types"
import { StudySummary } from "ts/types/optuna"
export const useCreateStudyDialog = (): [() => void, () => ReactNode] => {
const theme = useTheme()
@@ -25,7 +27,9 @@ export const useCreateStudyDialog = (): [() => void, () => ReactNode] => {
const [newStudyName, setNewStudyName] = useState("")
const [openNewStudyDialog, setOpenNewStudyDialog] = useState(false)
const [directions, setDirections] = useState<StudyDirection[]>(["minimize"])
const [directions, setDirections] = useState<Optuna.StudyDirection[]>([
"minimize",
])
const studies = useRecoilValue<StudySummary[]>(studySummariesState)
const newStudyNameAlreadyUsed = studies.some(
(v) => v.study_name === newStudyName
@@ -86,8 +90,8 @@ export const useCreateStudyDialog = (): [() => void, () => ReactNode] => {
<Select
value={directions[i]}
onChange={(e) => {
const newVal: StudyDirection[] = [...directions]
newVal[i] = e.target.value as StudyDirection
const newVal: Optuna.StudyDirection[] = [...directions]
newVal[i] = e.target.value as Optuna.StudyDirection
setDirections(newVal)
}}
>
@@ -103,7 +107,10 @@ export const useCreateStudyDialog = (): [() => void, () => ReactNode] => {
startIcon={<AddIcon />}
sx={{ marginRight: theme.spacing(1) }}
onClick={() => {
const newVal: StudyDirection[] = [...directions, "minimize"]
const newVal: Optuna.StudyDirection[] = [
...directions,
"minimize",
]
setDirections(newVal)
}}
>
@@ -115,7 +122,7 @@ export const useCreateStudyDialog = (): [() => void, () => ReactNode] => {
sx={{ marginRight: theme.spacing(1) }}
disabled={directions.length <= 1}
onClick={() => {
const newVal: StudyDirection[] = [...directions]
const newVal: Optuna.StudyDirection[] = [...directions]
newVal.pop()
setDirections(newVal)
}}
@@ -20,6 +20,7 @@ import { getAxisInfo } from "../graphUtil"
import { PlotType } from "../apiClient"
import { useBackendRender } from "../state"
import { usePlot } from "../hooks/usePlot"
import { SearchSpaceItem, StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-contour"
const CONTOUR_DISABLED_THRESHOLD = 100
@@ -206,13 +207,8 @@ const ContourFrontend: FC<{
)
}
const filterFunc = (trial: Trial, objectiveId: number): boolean => {
return (
trial.state === "Complete" &&
trial.values !== undefined &&
trial.values[objectiveId] !== "inf" &&
trial.values[objectiveId] !== "-inf"
)
const filterFunc = (trial: Trial): boolean => {
return trial.state === "Complete" && trial.values !== undefined
}
const plotContour = (
@@ -227,7 +223,7 @@ const plotContour = (
}
const trials: Trial[] = study ? study.trials : []
const filteredTrials = trials.filter((t) => filterFunc(t, objectiveId))
const filteredTrials = trials.filter((t) => filterFunc(t))
if (filteredTrials.length < 2 || xParam === null || yParam === null) {
plotly.react(plotDomId, [], {
template: colorTheme,
+4 -3
View File
@@ -4,6 +4,7 @@ import { Typography, useTheme, Box } from "@mui/material"
import { Target, useFilteredTrialsFromStudies } from "../trialFilter"
import { getCompareStudiesPlotAPI, CompareStudiesPlotType } from "../apiClient"
import { usePlotlyColorTheme, useBackendRender } from "../state"
import { StudyDetail, Trial } from "ts/types/optuna"
const getPlotDomId = (objectiveId: number) => `graph-edf-${objectiveId}`
@@ -45,7 +46,7 @@ const GraphEdfBackend: FC<{
console.error(err)
})
}, [studyIds, numCompletedTrials])
return <Box id={domId} sx={{ height: "450px" }} />
return <Box component="div" id={domId} sx={{ height: "450px" }} />
}
const GraphEdfFrontend: FC<{
@@ -74,14 +75,14 @@ const GraphEdfFrontend: FC<{
}, [studies, target, colorTheme])
return (
<Box>
<Box component="div">
<Typography
variant="h6"
sx={{ margin: "1em 0", fontWeight: theme.typography.fontWeightBold }}
>
{`EDF for ${target.toLabel(studies[0].objective_names)}`}
</Typography>
<Box id={domId} sx={{ height: "450px" }} />
<Box component="div" id={domId} sx={{ height: "450px" }} />
</Box>
)
}
@@ -22,13 +22,15 @@ import {
} from "../trialFilter"
import { usePlotlyColorTheme } from "../state"
import { useNavigate } from "react-router-dom"
import * as Optuna from "@optuna/types"
import { StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-history"
interface HistoryPlotInfo {
study_name: string
trials: Trial[]
directions: StudyDirection[]
directions: Optuna.StudyDirection[]
objective_names?: string[]
}
@@ -210,6 +212,7 @@ export const GraphHistory: FC<{
</Grid>
<Grid item xs={9}>
<Box
component="div"
id={plotDomId}
sx={{
height: "450px",
@@ -10,6 +10,7 @@ import {
} from "../state"
import { PlotType } from "../apiClient"
import { usePlot } from "../hooks/usePlot"
import { ParamImportance, StudyDetail } from "ts/types/optuna"
const plotDomId = "graph-hyperparameter-importances"
@@ -61,7 +62,7 @@ const GraphHyperparameterImportanceBackend: FC<{
}
}, [error])
return <Box id={plotDomId} sx={{ height: graphHeight }} />
return <Box component="div" id={plotDomId} sx={{ height: graphHeight }} />
}
const GraphHyperparameterImportanceFrontend: FC<{
@@ -99,7 +100,7 @@ const GraphHyperparameterImportanceFrontend: FC<{
>
Hyperparameter Importance
</Typography>
<Box id={plotDomId} sx={{ height: graphHeight }} />
<Box component="div" id={plotDomId} sx={{ height: graphHeight }} />
</CardContent>
</Card>
)
@@ -2,6 +2,7 @@ import * as plotly from "plotly.js-dist-min"
import React, { FC, useEffect } from "react"
import { Box, Typography, useTheme, CardContent, Card } from "@mui/material"
import { usePlotlyColorTheme } from "../state"
import { Trial } from "ts/types/optuna"
const plotDomId = "graph-intermediate-values"
@@ -26,7 +27,7 @@ export const GraphIntermediateValues: FC<{
>
Intermediate values
</Typography>
<Box id={plotDomId} sx={{ height: "450px" }} />
<Box component="div" id={plotDomId} sx={{ height: "450px" }} />
</CardContent>
</Card>
)
@@ -76,13 +77,10 @@ const plotIntermediateValue = (
t.state === "Running"
)
const plotData: Partial<plotly.PlotData>[] = filteredTrials.map((trial) => {
const values = trial.intermediate_values.filter(
(iv) => iv.value !== "inf" && iv.value !== "-inf" && iv.value !== "nan"
)
const isFeasible = trial.constraints.every((c) => c <= 0)
return {
x: values.map((iv) => iv.step),
y: values.map((iv) => iv.value),
x: trial.intermediate_values.map((iv) => iv.step),
y: trial.intermediate_values.map((iv) => iv.value),
marker: { maxdisplayed: 10 },
mode: "lines+markers",
type: "scatter",
@@ -20,6 +20,7 @@ import { useMergedUnionSearchSpace } from "../searchSpace"
import { PlotType } from "../apiClient"
import { useBackendRender } from "../state"
import { usePlot } from "../hooks/usePlot"
import { SearchSpaceItem, StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-parallel-coordinate"
@@ -121,7 +122,7 @@ const GraphParallelCoordinateBackend: FC<{
}
}, [error])
return <Box id={plotDomId} sx={{ height: "450px" }} />
return <Box component="div" id={plotDomId} sx={{ height: "450px" }} />
}
const GraphParallelCoordinateFrontend: FC<{
@@ -161,7 +162,7 @@ const GraphParallelCoordinateFrontend: FC<{
{renderCheckBoxes()}
</Grid>
<Grid item xs={9}>
<Box id={plotDomId} sx={{ height: "450px" }} />
<Box component="div" id={plotDomId} sx={{ height: "450px" }} />
</Grid>
</Grid>
)
@@ -246,7 +247,9 @@ const plotCoordinate = (
)
if (s.distribution.type === "CategoricalDistribution") {
// categorical
const vocabArr: string[] = s.distribution.choices.map((c) => c.value)
const vocabArr: string[] = s.distribution.choices.map(
(c) => c?.toString() ?? "null"
)
const tickvals: number[] = vocabArr.map((v, i) => i)
return {
label: breakLabelIfTooLong(s.name),
@@ -17,6 +17,8 @@ import { useNavigate } from "react-router-dom"
import { PlotType } from "../apiClient"
import { useBackendRender } from "../state"
import { usePlot } from "../hooks/usePlot"
import * as Optuna from "@optuna/types"
import { StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-pareto-front"
@@ -53,7 +55,7 @@ const GraphParetoFrontBackend: FC<{
}
}, [error])
return <Box id={plotDomId} sx={{ height: "450px" }} />
return <Box component="div" id={plotDomId} sx={{ height: "450px" }} />
}
const GraphParetoFrontFrontend: FC<{
@@ -149,6 +151,7 @@ const GraphParetoFrontFrontend: FC<{
</Grid>
<Grid item xs={9}>
<Box
component="div"
id={plotDomId}
sx={{
height: "450px",
@@ -159,12 +162,14 @@ const GraphParetoFrontFrontend: FC<{
)
}
const filterFunc = (trial: Trial, directions: StudyDirection[]): boolean => {
const filterFunc = (
trial: Trial,
directions: Optuna.StudyDirection[]
): boolean => {
return (
trial.state === "Complete" &&
trial.values !== undefined &&
trial.values.length === directions.length &&
trial.values.every((v) => v !== "inf" && v !== "-inf")
trial.values.length === directions.length
)
}
+4 -11
View File
@@ -16,6 +16,7 @@ import { useMergedUnionSearchSpace } from "../searchSpace"
import { PlotType } from "../apiClient"
import { usePlotlyColorTheme, useBackendRender } from "../state"
import { usePlot } from "../hooks/usePlot"
import { SearchSpaceItem, StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-rank"
@@ -65,7 +66,7 @@ const GraphRankBackend: FC<{
}
}, [error])
return <Box id={plotDomId} sx={{ height: "450px" }} />
return <Box component="div" id={plotDomId} sx={{ height: "450px" }} />
}
const GraphRankFrontend: FC<{
@@ -163,7 +164,7 @@ const GraphRankFrontend: FC<{
) : null}
</Grid>
<Grid item xs={9}>
<Box id={plotDomId} sx={{ height: "450px" }} />
<Box component="div" id={plotDomId} sx={{ height: "450px" }} />
</Grid>
</Grid>
)
@@ -193,21 +194,13 @@ const getRankPlotInfo = (
const zValues: number[] = []
const isFeasible: boolean[] = []
const hovertext: string[] = []
const convertTrialValueToNumber = (value: TrialValueNumber): number => {
// TrialValueNumber takes `number`, "inf", or "-inf".
return typeof value === "number"
? value
: value.includes("-")
? -Infinity
: Infinity
}
filteredTrials.forEach((trial, i) => {
const xValue = xAxis.values[i]
const yValue = yAxis.values[i]
if (xValue && yValue && trial.values) {
xValues.push(xValue)
yValues.push(yValue)
const zValue = convertTrialValueToNumber(trial.values[objectiveId])
const zValue = trial.values[objectiveId]
zValues.push(zValue)
const feasibility = trial.constraints.every((c) => c <= 0)
isFeasible.push(feasibility)
@@ -22,6 +22,7 @@ import { useMergedUnionSearchSpace } from "../searchSpace"
import { PlotType } from "../apiClient"
import { usePlotlyColorTheme, useBackendRender } from "../state"
import { usePlot } from "../hooks/usePlot"
import { SearchSpaceItem, StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-slice"
@@ -66,7 +67,7 @@ const GraphSliceBackend: FC<{
}
}, [error])
return <Box id={plotDomId} sx={{ height: "450px" }} />
return <Box component="div" id={plotDomId} sx={{ height: "450px" }} />
}
const GraphSliceFrontend: FC<{
@@ -175,7 +176,7 @@ const GraphSliceFrontend: FC<{
</FormControl>
</Grid>
<Grid item xs={9}>
<Box id={plotDomId} sx={{ height: "450px" }} />
<Box component="div" id={plotDomId} sx={{ height: "450px" }} />
</Grid>
</Grid>
)
@@ -291,7 +292,9 @@ const plotSlice = (
automargin: true, // Otherwise the label is outside of the plot
}
} else {
const vocabArr = selectedParamSpace.distribution.choices.map((c) => c.value)
const vocabArr = selectedParamSpace.distribution.choices.map(
(c) => c?.toString() ?? "null"
)
const tickvals: number[] = vocabArr.map((v, i) => i)
layout["xaxis"] = {
title: selectedParamTarget.toLabel(),
@@ -6,6 +6,8 @@ import { usePlotlyColorTheme } from "../state"
import { PlotType } from "../apiClient"
import { useBackendRender } from "../state"
import { usePlot } from "../hooks/usePlot"
import * as Optuna from "@optuna/types"
import { StudyDetail, Trial } from "ts/types/optuna"
const plotDomId = "graph-timeline"
const maxBars = 100
@@ -96,7 +98,7 @@ const plotTimeline = (
return
}
const cm: Record<TrialState, string> = {
const cm: Record<Optuna.TrialState, string> = {
Complete: "blue",
Fail: "red",
Pruned: "orange",
+16 -2
View File
@@ -31,6 +31,7 @@ import React, {
import ReactMarkdown from "react-markdown"
import remarkGfm from "remark-gfm"
import remarkMath from "remark-math"
// @ts-ignore
import rehypeMathjax from "rehype-mathjax"
import rehypeRaw from "rehype-raw"
import LoadingButton from "@mui/lab/LoadingButton"
@@ -41,6 +42,7 @@ import { Theme } from "@mui/material/styles"
import {
CodeComponent,
ReactMarkdownNames,
// @ts-ignore
} from "react-markdown/lib/ast-to-react"
import HtmlIcon from "@mui/icons-material/Html"
import ModeEditIcon from "@mui/icons-material/ModeEdit"
@@ -51,6 +53,7 @@ import { darcula } from "react-syntax-highlighter/dist/esm/styles/prism"
import { actionCreator } from "../action"
import { useRecoilValue } from "recoil"
import { artifactIsAvailable, isFileUploading, useArtifacts } from "../state"
import { Note } from "ts/types/optuna"
const placeholder = `## What is this feature for?
@@ -74,8 +77,11 @@ $$
`
const CodeBlock: CodeComponent | ReactMarkdownNames = ({
// @ts-ignore
inline,
// @ts-ignore
className,
// @ts-ignore
children,
...props
}) => {
@@ -296,6 +302,7 @@ const MarkdownEditorModal: FC<{
title="Markdown Editor"
/>
<Box
component="div"
sx={{
flexGrow: 1,
padding: theme.spacing(2),
@@ -306,6 +313,7 @@ const MarkdownEditorModal: FC<{
<MarkdownRenderer body={previewMarkdown} />
</Box>
<Box
component="div"
sx={{
width: "100%",
flexGrow: 1,
@@ -345,7 +353,10 @@ const MarkdownEditorModal: FC<{
/>
)}
</Box>
<Box sx={{ display: "flex", flexDirection: "row", alignItems: "center" }}>
<Box
component="div"
sx={{ display: "flex", flexDirection: "row", alignItems: "center" }}
>
{notLatest && !saving && (
<>
<Typography
@@ -369,7 +380,7 @@ const MarkdownEditorModal: FC<{
</Button>
</>
)}
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
onClick={() => {
@@ -452,6 +463,7 @@ const ArtifactUploader: FC<{
return (
<Box
component="div"
sx={{
width: "300px",
padding: theme.spacing(0, 1),
@@ -486,6 +498,7 @@ const ArtifactUploader: FC<{
style={{ display: "none" }}
/>
<Box
component="div"
sx={{
border: dragOver
? `3px dashed ${theme.palette.mode === "dark" ? "white" : "black"}`
@@ -499,6 +512,7 @@ const ArtifactUploader: FC<{
>
{dragOver && (
<Box
component="div"
sx={{
width: "100%",
height: "100%",
@@ -12,6 +12,7 @@ import { DataGrid, DataGridColumn } from "../DataGrid"
import { BestTrialsCard } from "../BestTrialsCard"
import { useStudyDetailValue, useStudySummaryValue } from "../../state"
import { Contour } from "../GraphContour"
import * as Optuna from "@optuna/types"
export const PreferentialAnalytics: FC<{ studyId: number }> = ({ studyId }) => {
const theme = useTheme()
@@ -19,7 +20,7 @@ export const PreferentialAnalytics: FC<{ studyId: number }> = ({ studyId }) => {
const studyDetail = useStudyDetailValue(studyId)
const userAttrs = studySummary?.user_attrs || studyDetail?.user_attrs || []
const userAttrColumns: DataGridColumn<Attribute>[] = [
const userAttrColumns: DataGridColumn<Optuna.Attribute>[] = [
{ field: "key", label: "Key", sortable: true },
{ field: "value", label: "Value", sortable: true },
]
@@ -54,7 +55,7 @@ export const PreferentialAnalytics: FC<{ studyId: number }> = ({ studyId }) => {
>
Study User Attributes
</Typography>
<DataGrid<Attribute>
<DataGrid<Optuna.Attribute>
columns={userAttrColumns}
rows={userAttrs}
keyField={"key"}
@@ -26,6 +26,7 @@ import { ElkNode } from "elkjs/lib/elk-api.js"
import { useStudyDetailValue } from "../../state"
import { getArtifactUrlPath } from "./PreferentialTrials"
import { PreferentialOutputComponent } from "./PreferentialOutputComponent"
import { StudyDetail, Trial } from "ts/types/optuna"
const elk = new ELK()
const nodeWidth = 400
@@ -67,6 +68,7 @@ const GraphNode: FC<NodeProps<NodeData>> = ({ data, isConnectable }) => {
}}
>
<Box
component="div"
sx={{
display: "flex",
displayDirection: "row",
@@ -21,6 +21,7 @@ import { formatDate } from "../../dateUtil"
import { actionCreator } from "../../action"
import { useStudyDetailValue } from "../../state"
import { PreferentialOutputComponent } from "./PreferentialOutputComponent"
import { PreferenceHistory, StudyDetail, Trial } from "ts/types/optuna"
type TrialType = "worst" | "none"
@@ -93,6 +94,7 @@ const CandidateTrial: FC<{
</CardActions>
<CardContent aria-label="trial" sx={cardComponentSx}>
<Box
component="div"
sx={{
padding: theme.spacing(2),
}}
@@ -126,6 +128,7 @@ const CandidateTrial: FC<{
</CardContent>
<Modal open={detailShown} onClose={() => setDetailShown(false)}>
<Box
component="div"
sx={{
position: "absolute",
top: 0,
@@ -141,6 +144,7 @@ const CandidateTrial: FC<{
}}
>
<Box
component="div"
sx={{
width: "100%",
height: "100%",
@@ -182,12 +186,14 @@ const ChoiceTrials: FC<{
return (
<Box
component="div"
sx={{
marginBottom: theme.spacing(4),
position: "relative",
}}
>
<Box
component="div"
sx={{
display: "flex",
flexDirection: "row",
@@ -232,6 +238,7 @@ const ChoiceTrials: FC<{
)}
</Box>
<Box
component="div"
sx={{
display: "flex",
flexDirection: "row",
@@ -281,6 +288,7 @@ export const PreferentialHistory: FC<{ studyDetail: StudyDetail | null }> = ({
return (
<Box
component="div"
padding={theme.spacing(2)}
sx={{ display: "flex", flexDirection: "column" }}
>
@@ -1,6 +1,7 @@
import React, { FC, useMemo } from "react"
import { ArtifactCardMedia } from "../Artifact/ArtifactCardMedia"
import { MarkdownRenderer } from "../Note"
import { Artifact, FeedbackComponentType, Trial } from "ts/types/optuna"
export const PreferentialOutputComponent: FC<{
trial: Trial
@@ -34,6 +34,14 @@ import {
useThreejsArtifactModal,
} from "../Artifact/ThreejsArtifactViewer"
import { PreferentialOutputComponent } from "./PreferentialOutputComponent"
import {
Artifact,
FeedbackComponentArtifact,
FeedbackComponentNote,
FeedbackComponentType,
StudyDetail,
Trial,
} from "ts/types/optuna"
const SettingsPage: FC<{
studyDetail: StudyDetail
@@ -216,6 +224,7 @@ const PreferentialTrial: FC<{
if (trial === undefined) {
return (
<Box
component="div"
sx={{
width: trialWidth,
minHeight: trialHeight,
@@ -244,6 +253,7 @@ const PreferentialTrial: FC<{
>
<CardActions>
<Box
component="div"
sx={{
margin: theme.spacing(0, 2),
maxWidth: `calc(${trialWidth}px - ${
@@ -322,6 +332,7 @@ const PreferentialTrial: FC<{
urlPath={urlPath}
/>
<Box
component="div"
sx={{
position: "absolute",
top: 0,
@@ -491,8 +502,8 @@ export const PreferentialTrials: FC<{ studyDetail: StudyDetail | null }> = ({
null
return (
<Box padding={theme.spacing(2)}>
<Box display="flex">
<Box component="div" padding={theme.spacing(2)}>
<Box component="div" display="flex">
<Typography
variant="h4"
sx={{
@@ -502,6 +513,7 @@ export const PreferentialTrials: FC<{ studyDetail: StudyDetail | null }> = ({
Which trial is the worst?
</Typography>
<Box
component="div"
display="flex"
sx={{
marginLeft: "auto",
@@ -541,7 +553,10 @@ export const PreferentialTrials: FC<{ studyDetail: StudyDetail | null }> = ({
</Button>
</Box>
</Box>
<Box sx={{ display: "flex", flexDirection: "row", flexWrap: "wrap" }}>
<Box
component="div"
sx={{ display: "flex", flexDirection: "row", flexWrap: "wrap" }}
>
{displayTrials.display.map((t, index) => {
const trial = activeTrials.find((trial) => trial.number === t)
const candidates = displayTrials.display.filter(
@@ -573,6 +588,7 @@ export const PreferentialTrials: FC<{ studyDetail: StudyDetail | null }> = ({
{detailTrial !== null && (
<Modal open={true} onClose={() => setDetailTrial(null)}>
<Box
component="div"
sx={{
position: "absolute",
top: 0,
@@ -588,6 +604,7 @@ export const PreferentialTrials: FC<{ studyDetail: StudyDetail | null }> = ({
}}
>
<Box
component="div"
sx={{
width: "100%",
height: "100%",
@@ -10,6 +10,7 @@ import {
} from "@mui/material"
import { actionCreator } from "../action"
import { DebouncedInputTextField } from "./Debounce"
import { StudySummary } from "ts/types/optuna"
export const useRenameStudyDialog = (
studies: StudySummary[]
@@ -12,6 +12,7 @@ import {
} from "@mui/material"
import ClearIcon from "@mui/icons-material/Clear"
import { usePlotBackendRendering, usePlotlyColorThemeState } from "../state"
import { PlotlyColorThemeDark, PlotlyColorThemeLight } from "ts/types/optuna"
interface SettingsProps {
handleClose: () => void
+16 -8
View File
@@ -35,12 +35,12 @@ import { PreferentialHistory } from "./Preferential/PreferentialHistory"
import { PreferentialAnalytics } from "./Preferential/PreferentialAnalytics"
import { PreferentialGraph } from "./Preferential/PreferentialGraph"
interface ParamTypes {
studyId: string
}
export const useURLVars = (): number => {
const { studyId } = useParams<ParamTypes>()
const { studyId } = useParams<{ studyId: string }>()
if (studyId === undefined) {
throw new Error("studyId is not defined")
}
return useMemo(() => parseInt(studyId, 10), [studyId])
}
@@ -102,7 +102,10 @@ export const StudyDetail: FC<{
content = isPreferential ? (
<PreferentialAnalytics studyId={studyId} />
) : (
<Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}>
<Box
component="div"
sx={{ display: "flex", width: "100%", flexDirection: "column" }}
>
<Typography
variant="h5"
sx={{
@@ -162,7 +165,10 @@ export const StudyDetail: FC<{
content = <TrialList studyDetail={studyDetail} />
} else if (page === "trialTable") {
content = (
<Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}>
<Box
component="div"
sx={{ display: "flex", width: "100%", flexDirection: "column" }}
>
<Card sx={{ margin: theme.spacing(2) }}>
<CardContent>
<TrialTable studyDetail={studyDetail} initialRowsPerPage={50} />
@@ -173,6 +179,7 @@ export const StudyDetail: FC<{
} else if (page === "note" && studyDetail !== null) {
content = (
<Box
component="div"
sx={{
height: `calc(100vh - ${theme.spacing(8)})`,
display: "flex",
@@ -199,6 +206,7 @@ export const StudyDetail: FC<{
} else if (page === "graph") {
content = (
<Box
component="div"
sx={{
height: `calc(100vh - ${theme.spacing(8)})`,
padding: theme.spacing(2),
@@ -234,7 +242,7 @@ export const StudyDetail: FC<{
)
return (
<Box sx={{ display: "flex" }}>
<Box component="div" sx={{ display: "flex" }}>
<AppDrawer
studyId={studyId}
page={page}
@@ -26,6 +26,8 @@ import {
} from "../state"
import FormControlLabel from "@mui/material/FormControlLabel"
import { artifactIsAvailable } from "../state"
import * as Optuna from "@optuna/types"
import { Trial } from "ts/types/optuna"
export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => {
const theme = useTheme()
@@ -45,13 +47,16 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => {
}
const userAttrs = studySummary?.user_attrs || studyDetail?.user_attrs || []
const userAttrColumns: DataGridColumn<Attribute>[] = [
const userAttrColumns: DataGridColumn<Optuna.Attribute>[] = [
{ field: "key", label: "Key", sortable: true },
{ field: "value", label: "Value", sortable: true },
]
const trials: Trial[] = studyDetail?.trials || []
return (
<Box sx={{ display: "flex", width: "100%", flexDirection: "column" }}>
<Box
component="div"
sx={{ display: "flex", width: "100%", flexDirection: "column" }}
>
<FormControl
component="fieldset"
sx={{
@@ -148,7 +153,7 @@ export const StudyHistory: FC<{ studyId: number }> = ({ studyId }) => {
>
Study User Attributes
</Typography>
<DataGrid<Attribute>
<DataGrid<Optuna.Attribute>
columns={userAttrColumns}
rows={userAttrs}
keyField={"key"}
+9 -6
View File
@@ -39,6 +39,7 @@ import { useCreateStudyDialog } from "./CreateStudyDialog"
import { useDeleteStudyDialog } from "./DeleteStudyDialog"
import { useRenameStudyDialog } from "./RenameStudyDialog"
import { useQuery } from "../urlQuery"
import { StudySummary } from "ts/types/optuna"
export const StudyList: FC<{
toggleColorMode: () => void
@@ -97,6 +98,7 @@ export const StudyList: FC<{
}))
const sortBySelect = (
<Box
component="div"
sx={{
position: "relative",
borderRadius: theme.shape.borderRadius,
@@ -104,6 +106,7 @@ export const StudyList: FC<{
}}
>
<Box
component="div"
sx={{
padding: theme.spacing(0, 2),
height: "100%",
@@ -134,7 +137,7 @@ export const StudyList: FC<{
let studyListContent
if (isLoading) {
studyListContent = (
<Box sx={{ margin: theme.spacing(2) }}>
<Box component="div" sx={{ margin: theme.spacing(2) }}>
<SvgIcon fontSize="small" color="action">
<HourglassTop />
</SvgIcon>
@@ -170,7 +173,7 @@ export const StudyList: FC<{
</CardContent>
</CardActionArea>
<CardActions disableSpacing sx={{ paddingTop: 0 }}>
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
<IconButton
aria-label="rename study"
size="small"
@@ -197,7 +200,7 @@ export const StudyList: FC<{
}
return (
<Box sx={{ display: "flex" }}>
<Box component="div" sx={{ display: "flex" }}>
<AppDrawer toggleColorMode={toggleColorMode} toolbar={toolbar}>
<Container
sx={{
@@ -208,7 +211,7 @@ export const StudyList: FC<{
>
<Card sx={{ margin: theme.spacing(2) }}>
<CardContent>
<Box sx={{ display: "flex" }}>
<Box component="div" sx={{ display: "flex" }}>
<TextField
onChange={(e) => {
setStudyFilterText(e.target.value)
@@ -229,7 +232,7 @@ export const StudyList: FC<{
}}
/>
{sortBySelect}
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
<Button
variant="outlined"
startIcon={<Refresh />}
@@ -262,7 +265,7 @@ export const StudyList: FC<{
</Box>
</CardContent>
</Card>
<Box sx={{ display: "flex", flexWrap: "wrap" }}>
<Box component="div" sx={{ display: "flex", flexWrap: "wrap" }}>
{studyListContent}
</Box>
</Container>
@@ -16,6 +16,16 @@ import {
import { DebouncedInputTextField } from "./Debounce"
import { actionCreator } from "../action"
import { useTrialUpdatingValue } from "../state"
import * as Optuna from "@optuna/types"
import {
FormWidgets,
ObjectiveChoiceWidget,
ObjectiveSliderWidget,
ObjectiveTextInputWidget,
ObjectiveUserAttrRef,
Trial,
UserAttrFormWidget,
} from "ts/types/optuna"
type WidgetState = {
isValid: boolean
@@ -26,7 +36,7 @@ type WidgetState = {
export const TrialFormWidgets: FC<{
trial: Trial
objectiveNames: string[]
directions: StudyDirection[]
directions: Optuna.StudyDirection[]
formWidgets?: FormWidgets
}> = ({ trial, objectiveNames, directions, formWidgets }) => {
if (
@@ -136,7 +146,7 @@ const UpdatableFormWidgets: FC<{
}
return (
<Box sx={{ p: theme.spacing(1, 0) }}>
<Box component="div" sx={{ p: theme.spacing(1, 0) }}>
<Card
sx={{
display: "flex",
@@ -149,6 +159,7 @@ const UpdatableFormWidgets: FC<{
>
{widgetStates.map((ws) => ws.render())}
<Box
component="div"
sx={{
display: "flex",
flexDirection: "row",
@@ -283,7 +294,7 @@ export const useSliderWidget = (
<FormLabel>
{metricName} - {widget.description}
</FormLabel>
<Box sx={{ padding: theme.spacing(0, 2) }}>
<Box component="div" sx={{ padding: theme.spacing(0, 2) }}>
<Slider
onChange={(e) => {
// @ts-ignore
@@ -348,7 +359,7 @@ const ReadonlyFormWidgets: FC<{
formWidgets: FormWidgets
}> = ({ trial, widgetNames, formWidgets }) => {
const theme = useTheme()
const getValue = (i: number): string | TrialValueNumber => {
const getValue = (i: number): string | number => {
if (formWidgets.output_type === "user_attr") {
const widget = formWidgets.widgets[i] as UserAttrFormWidget
return (
@@ -369,7 +380,7 @@ const ReadonlyFormWidgets: FC<{
}
return (
<Box sx={{ p: theme.spacing(1, 0) }}>
<Box component="div" sx={{ p: theme.spacing(1, 0) }}>
<Card
sx={{
display: "flex",
@@ -424,11 +435,9 @@ const ReadonlyFormWidgets: FC<{
<FormLabel>
{widgetName} - {widget.description}
</FormLabel>
<Box sx={{ padding: theme.spacing(0, 2) }}>
<Box component="div" sx={{ padding: theme.spacing(0, 2) }}>
<Slider
defaultValue={
value === "inf" || value === "-inf" ? undefined : value
}
defaultValue={value}
min={widget.min}
max={widget.max}
step={widget.step}
+36 -19
View File
@@ -19,6 +19,7 @@ import FilterListIcon from "@mui/icons-material/FilterList"
import CheckBoxOutlineBlankIcon from "@mui/icons-material/CheckBoxOutlineBlank"
import CheckBoxIcon from "@mui/icons-material/CheckBox"
import StopCircleIcon from "@mui/icons-material/StopCircle"
import * as Optuna from "@optuna/types"
import { TrialNote } from "./Note"
import { useNavigate } from "react-router-dom"
@@ -29,10 +30,10 @@ import { actionCreator } from "../action"
import { TrialFormWidgets } from "./TrialFormWidgets"
import { TrialArtifactCards } from "./Artifact/TrialArtifactCards"
import { useQuery } from "../urlQuery"
import { useVirtualizer } from "@tanstack/react-virtual"
import { FormWidgets, StudyDetail, Trial } from "ts/types/optuna"
const states: TrialState[] = [
const states: Optuna.TrialState[] = [
"Complete",
"Pruned",
"Fail",
@@ -49,7 +50,7 @@ type Color =
| "success"
| "warning"
const getChipColor = (state: TrialState): Color => {
const getChipColor = (state: Optuna.TrialState): Color => {
if (state === "Complete") {
return "primary"
} else if (state === "Running") {
@@ -64,25 +65,25 @@ const getChipColor = (state: TrialState): Color => {
return "default"
}
const useExcludedStates = (query: URLSearchParams): TrialState[] => {
const useExcludedStates = (query: URLSearchParams): Optuna.TrialState[] => {
return useMemo(() => {
const exclude = query.get("exclude")
if (exclude === null) {
return []
}
const excluded: TrialState[] = exclude
const excluded: Optuna.TrialState[] = exclude
.split(",")
.map((s): TrialState | undefined => {
.map((s): Optuna.TrialState | undefined => {
return states.find((state) => state.toUpperCase() === s.toUpperCase())
})
.filter((s): s is TrialState => s !== undefined)
.filter((s): s is Optuna.TrialState => s !== undefined)
return excluded
}, [query])
}
const useTrials = (
studyDetail: StudyDetail | null,
excludedStates: TrialState[]
excludedStates: Optuna.TrialState[]
): Trial[] => {
return useMemo(() => {
let result = studyDetail !== null ? studyDetail.trials : []
@@ -123,7 +124,7 @@ const useIsBestTrial = (
export const TrialListDetail: FC<{
trial: Trial
isBestTrial: (trialId: number) => boolean
directions: StudyDirection[]
directions: Optuna.StudyDirection[]
objectiveNames: string[]
formWidgets?: FormWidgets
}> = ({ trial, isBestTrial, directions, objectiveNames, formWidgets }) => {
@@ -138,7 +139,7 @@ export const TrialListDetail: FC<{
["Value", trial.values?.map((v) => v.toString()).join(", ") || "None"],
[
"Intermediate Values",
<Box>
<Box component="div">
{trial.intermediate_values.map((v) => (
<Typography key={v.step}>
{v.step} {v.value}
@@ -148,7 +149,7 @@ export const TrialListDetail: FC<{
],
[
"Parameter",
<Box>
<Box component="div">
{params.map((p) => (
<Typography key={p.name}>
{p.name} {p.param_external_value}
@@ -172,7 +173,7 @@ export const TrialListDetail: FC<{
],
[
"User Attributes",
<Box>
<Box component="div">
{trial.user_attrs.map((t) => (
<Typography key={t.key}>
{t.key} {t.value}
@@ -186,6 +187,7 @@ export const TrialListDetail: FC<{
value: string | null | ReactNode
): ReactNode => (
<Box
component="div"
key={key}
sx={{
display: "flex",
@@ -203,6 +205,7 @@ export const TrialListDetail: FC<{
{key}
</Typography>
<Box
component="div"
sx={{
bgcolor:
theme.palette.mode === "dark"
@@ -223,7 +226,10 @@ export const TrialListDetail: FC<{
)
return (
<Box sx={{ width: "100%", padding: theme.spacing(2, 2, 0, 2) }}>
<Box
component="div"
sx={{ width: "100%", padding: theme.spacing(2, 2, 0, 2) }}
>
<Typography
variant="h4"
sx={{
@@ -234,6 +240,7 @@ export const TrialListDetail: FC<{
Trial {trial.number} (trial_id={trial.trial_id})
</Typography>
<Box
component="div"
sx={{
marginBottom: theme.spacing(1),
display: "flex",
@@ -249,7 +256,7 @@ export const TrialListDetail: FC<{
{isBestTrial(trial.trial_id) ? (
<Chip label={"Best Trial"} color="secondary" variant="outlined" />
) : null}
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
{trial.state === "Running" ? (
<Button
variant="outlined"
@@ -286,6 +293,7 @@ export const TrialListDetail: FC<{
formWidgets={formWidgets}
/>
<Box
component="div"
sx={{
marginBottom: theme.spacing(2),
display: "flex",
@@ -303,7 +311,7 @@ export const TrialListDetail: FC<{
const getTrialListLink = (
studyId: number,
exclude: TrialState[],
exclude: Optuna.TrialState[],
numbers: number[]
): string => {
const base = URL_PREFIX + `/studies/${studyId}/trials`
@@ -356,8 +364,12 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({
queried.length > 0 ? queried : trials.length > 0 ? [trials[0]] : []
return (
<Box sx={{ display: "flex", flexDirection: "row", width: "100%" }}>
<Box
component="div"
sx={{ display: "flex", flexDirection: "row", width: "100%" }}
>
<Box
component="div"
ref={listParentRef}
sx={{
minWidth: trialListWidth,
@@ -370,7 +382,7 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({
<Typography sx={{ p: theme.spacing(1, 0) }}>
{trials.length} Trials
</Typography>
<Box sx={{ flexGrow: 1 }} />
<Box component="div" sx={{ flexGrow: 1 }} />
<IconButton
aria-label="Filter"
aria-controls={openFilterMenu ? "filter-trials" : undefined}
@@ -424,6 +436,7 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({
</ListSubheader>
<Divider />
<Box
component="div"
sx={{
width: "100%",
height: `${rowVirtualizer.getTotalSize()}px`,
@@ -485,7 +498,7 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({
}}
>
<ListItemText primary={`Trial ${trial.number}`} />
<Box>
<Box component="div">
<Chip
color={getChipColor(trial.state)}
label={trial.state}
@@ -512,13 +525,17 @@ export const TrialList: FC<{ studyDetail: StudyDetail | null }> = ({
</Box>
<Divider orientation="vertical" flexItem />
<Box
component="div"
sx={{
flexGrow: 1,
overflow: "auto",
height: `calc(100vh - ${theme.spacing(8)})`,
}}
>
<Box sx={{ display: "flex", flexDirection: "row", width: "100%" }}>
<Box
component="div"
sx={{ display: "flex", flexDirection: "row", width: "100%" }}
>
{selected.length === 0
? null
: selected.map((t) => (
@@ -5,6 +5,7 @@ import DownloadIcon from "@mui/icons-material/Download"
import { DataGridColumn, DataGrid } from "./DataGrid"
import { Link } from "react-router-dom"
import { StudyDetail, Trial } from "ts/types/optuna"
export const TrialTable: FC<{
studyDetail: StudyDetail | null
@@ -26,8 +27,8 @@ export const TrialTable: FC<{
},
]
const valueComparator = (
firstVal?: TrialValueNumber,
secondVal?: TrialValueNumber,
firstVal?: number,
secondVal?: number,
ascending: boolean = true
): number => {
if (firstVal === secondVal) {
@@ -38,11 +39,6 @@ export const TrialTable: FC<{
} else if (secondVal === undefined) {
return ascending ? 1 : -1
}
if (firstVal === "-inf" || secondVal === "inf") {
return 1
} else if (secondVal === "-inf" || firstVal === "inf") {
return -1
}
return firstVal < secondVal ? 1 : -1
}
if (studyDetail === null || studyDetail.directions.length === 1) {
@@ -96,7 +92,7 @@ export const TrialTable: FC<{
const sortable = s.distribution.type !== "CategoricalDistribution"
const filterChoices: (string | null)[] | undefined =
s.distribution.type === "CategoricalDistribution"
? s.distribution.choices.map((c) => c.value)
? s.distribution.choices.map((c) => c?.toString() ?? "null")
: undefined
const hasMissingValue = trials.some(
(t) => !t.params.some((p) => p.name === s.name)
@@ -1,6 +1,7 @@
import * as plotly from "plotly.js-dist-min"
import React, { FC, useEffect } from "react"
import { Box } from "@mui/material"
import { PlotlyGraphObject } from "ts/types/optuna"
export const UserDefinedPlot: FC<{
graphObject: PlotlyGraphObject
@@ -17,5 +18,5 @@ export const UserDefinedPlot: FC<{
}
}, [graphObject])
return <Box id={plotDomId} sx={{ height: "450px" }} />
return <Box component="div" id={plotDomId} sx={{ height: "450px" }} />
}
+1 -1
View File
@@ -1,5 +1,5 @@
export const formatDate = (date: Date): string => {
const options = {
const options: Intl.DateTimeFormatOptions = {
year: "numeric",
month: "numeric",
day: "numeric",
+9 -4
View File
@@ -1,15 +1,20 @@
const filterFunc = (trial: Trial, directions: StudyDirection[]): boolean => {
import * as Optuna from "@optuna/types"
import { Trial } from "./types/optuna"
const filterFunc = (
trial: Trial,
directions: Optuna.StudyDirection[]
): boolean => {
return (
trial.state === "Complete" &&
trial.values !== undefined &&
trial.values.length === directions.length &&
trial.values.every((v) => v !== "inf" && v !== "-inf")
trial.values.length === directions.length
)
}
export const getDominatedTrials = (
trials: Trial[],
directions: StudyDirection[]
directions: Optuna.StudyDirection[]
): Trial[] => {
// TODO(c-bata): Use log-linear algorithm like Optuna.
// TODO(c-bata): Use this function at GraphParetoFront.
+6 -3
View File
@@ -1,3 +1,6 @@
import { SearchSpaceItem, Trial } from "./types/optuna"
import * as Optuna from "@optuna/types"
const PADDING_RATIO = 0.05
export type AxisInfo = {
@@ -33,7 +36,7 @@ export const getAxisInfo = (
const getAxisInfoForCategoricalParams = (
trials: Trial[],
paramName: string,
distribution: CategoricalDistribution
distribution: Optuna.CategoricalDistribution
): AxisInfo => {
const values = trials.map(
(trial) =>
@@ -42,7 +45,7 @@ const getAxisInfoForCategoricalParams = (
)
const indices = distribution.choices
.map((c) => c.value)
.map((c) => c?.toString() ?? "null")
.sort((a, b) =>
a.toLowerCase() < b.toLowerCase()
? -1
@@ -62,7 +65,7 @@ const getAxisInfoForCategoricalParams = (
const getAxisInfoForNumericalParams = (
trials: Trial[],
paramName: string,
distribution: FloatDistribution | IntDistribution
distribution: Optuna.FloatDistribution | Optuna.IntDistribution
): AxisInfo => {
let min = 0
let max = 0
@@ -3,6 +3,7 @@ import { useSnackbar } from "notistack"
import { getParamImportances } from "../apiClient"
import { useQuery } from "@tanstack/react-query"
import { AxiosError } from "axios"
import { ParamImportance } from "ts/types/optuna"
export const useParamImportance = ({
numCompletedTrials,
+5 -3
View File
@@ -1,9 +1,11 @@
import { useMemo } from "react"
import { SearchSpaceItem } from "./types/optuna"
import * as Optuna from "@optuna/types"
export const mergeUnionSearchSpace = (
unionSearchSpace: SearchSpaceItem[]
): SearchSpaceItem[] => {
const knownElements = new Map<string, Distribution>()
const knownElements = new Map<string, Optuna.Distribution>()
unionSearchSpace.forEach((s) => {
const d = knownElements.get(s.name)
if (d === undefined) {
@@ -17,7 +19,7 @@ export const mergeUnionSearchSpace = (
// CategoricalDistribution.choices will never be changed
return
}
const updated: Distribution = {
const updated: Optuna.Distribution = {
...d,
low: Math.min(d.low, s.distribution.low),
high: Math.max(d.high, s.distribution.high),
@@ -28,7 +30,7 @@ export const mergeUnionSearchSpace = (
.sort((a, b) => (a > b ? 1 : a < b ? -1 : 0))
.map((name) => ({
name: name,
distribution: knownElements.get(name) as Distribution,
distribution: knownElements.get(name) as Optuna.Distribution,
}))
}
+9 -1
View File
@@ -1,9 +1,17 @@
import * as Optuna from "@optuna/types"
import { atom, useRecoilValue } from "recoil"
import {
LightColorTemplates,
DarkColorTemplates,
} from "./components/PlotlyColorTemplates"
import { useLocalStorage } from "usehooks-ts"
import {
Artifact,
PlotlyColorTheme,
StudyDetail,
StudyDetails,
StudySummary,
} from "./types/optuna"
export const studySummariesState = atom<StudySummary[]>({
key: "studySummaries",
@@ -86,7 +94,7 @@ export const useTrialUpdatingValue = (trialId: number): boolean => {
export const useStudyDirections = (
studyId: number
): StudyDirection[] | null => {
): Optuna.StudyDirection[] | null => {
const studyDetail = useStudyDetailValue(studyId)
const studySummary = useStudySummaryValue(studyId)
return studyDetail?.directions || studySummary?.directions || null
+8 -3
View File
@@ -1,4 +1,6 @@
import { useMemo, useState } from "react"
import * as Optuna from "@optuna/types"
import { SearchSpaceItem, StudyDetail, Trial } from "./types/optuna"
type TargetKind = "objective" | "user_attr" | "params"
@@ -67,7 +69,7 @@ export class Target {
return null
}
const value = trial.values[objectiveId]
if (value === "inf" || value === "-inf") {
if (value === Infinity || value === -Infinity) {
return null
}
return value
@@ -209,8 +211,11 @@ export const useObjectiveAndUserAttrTargetsFromStudies = (
}, Number.MAX_VALUE)
}, [studies])
const intersect = (arrays: AttributeSpec[][]) => {
const atrEqual = (obj1: AttributeSpec, obj2: AttributeSpec) => {
const intersect = (arrays: Optuna.AttributeSpec[][]) => {
const atrEqual = (
obj1: Optuna.AttributeSpec,
obj2: Optuna.AttributeSpec
) => {
return obj1.key === obj2.key
}
return arrays.reduce((a, b) =>
-237
View File
@@ -6,240 +6,3 @@ declare module "*.svg"
declare const APP_BAR_TITLE: string
declare const API_ENDPOINT: string
declare const URL_PREFIX: string
type TrialValueNumber = number | "inf" | "-inf"
type TrialIntermediateValueNumber = number | "inf" | "-inf" | "nan"
type TrialState = "Running" | "Complete" | "Pruned" | "Fail" | "Waiting"
type TrialStateFinished = "Complete" | "Fail" | "Pruned"
type StudyDirection = "maximize" | "minimize" | "not_set"
type PreferenceFeedbackMode = "ChooseWorst"
type FloatDistribution = {
type: "FloatDistribution"
low: number
high: number
step: number
log: boolean
}
type IntDistribution = {
type: "IntDistribution"
low: number
high: number
step: number
log: boolean
}
type CategoricalDistribution = {
type: "CategoricalDistribution"
choices: { pytype: string; value: string }[]
}
type Distribution =
| FloatDistribution
| IntDistribution
| CategoricalDistribution
type GraphVisibility = {
history: boolean
paretoFront: boolean
parallelCoordinate: boolean
intermediateValues: boolean
edf: boolean
contour: boolean
importances: boolean
slice: boolean
}
type TrialIntermediateValue = {
step: number
value: TrialIntermediateValueNumber
}
type TrialParam = {
name: string
param_internal_value: number
param_external_value: string
param_external_type: string
distribution: Distribution
}
type ParamImportance = {
name: string
importance: number
distribution: Distribution
}
type SearchSpaceItem = {
name: string
distribution: Distribution
}
type Attribute = {
key: string
value: string
}
type AttributeSpec = {
key: string
sortable: boolean
}
type Note = {
version: number
body: string
}
type Artifact = {
artifact_id: string
filename: string
mimetype: string
encoding: string
}
type Trial = {
trial_id: number
study_id: number
number: number
state: TrialState
values?: TrialValueNumber[]
intermediate_values: TrialIntermediateValue[]
datetime_start?: Date
datetime_complete?: Date
params: TrialParam[]
fixed_params: {
name: string
param_external_value: string
}[]
user_attrs: Attribute[]
constraints: number[]
note: Note
artifacts: Artifact[]
}
type StudySummary = {
study_id: number
study_name: string
directions: StudyDirection[]
user_attrs: Attribute[]
is_preferential: boolean
datetime_start?: Date
}
type ObjectiveChoiceWidget = {
type: "choice"
description: string
user_attr_key?: string
choices: string[]
values: number[]
}
type ObjectiveSliderWidget = {
type: "slider"
description: string
user_attr_key?: string
min: number
max: number
step: number | null
labels:
| {
value: number
label: string
}[]
| null
}
type ObjectiveTextInputWidget = {
type: "text"
description: string
optional: boolean
user_attr_key?: string
}
type ObjectiveUserAttrRef = {
type: "user_attr"
key: string
}
type ObjectiveFormWidget =
| ObjectiveChoiceWidget
| ObjectiveSliderWidget
| ObjectiveTextInputWidget
| ObjectiveUserAttrRef
type UserAttrFormWidget =
| ObjectiveChoiceWidget
| ObjectiveSliderWidget
| ObjectiveTextInputWidget
type FormWidgets =
| {
output_type: "objective"
widgets: ObjectiveFormWidget[]
}
| {
output_type: "user_attr"
widgets: UserAttrFormWidget[]
}
type PlotlyGraphObject = {
id: string
graph_object: string
}
type FeedbackComponentNote = {
output_type: "note"
}
type FeedbackComponentArtifact = {
output_type: "artifact"
artifact_key: string
}
type FeedbackComponentType = FeedbackComponentArtifact | FeedbackComponentNote
type StudyDetail = {
id: number
name: string
directions: StudyDirection[]
user_attrs: Attribute[]
datetime_start: Date
best_trials: Trial[]
trials: Trial[]
intersection_search_space: SearchSpaceItem[]
union_search_space: SearchSpaceItem[]
union_user_attrs: AttributeSpec[]
has_intermediate_values: boolean
note: Note
is_preferential: boolean
objective_names?: string[]
form_widgets?: FormWidgets
feedback_component_type: FeedbackComponentType
preferences?: [number, number][]
preference_history?: PreferenceHistory[]
plotly_graph_objects: PlotlyGraphObject[]
artifacts: Artifact[]
skipped_trial_numbers: number[]
}
type StudyDetails = {
[study_id: string]: StudyDetail
}
type PreferenceHistory = {
id: string
candidates: number[]
clicked: number
feedback_mode: PreferenceFeedbackMode
timestamp: Date
preferences: [number, number][]
is_removed: boolean
}
type PlotlyColorThemeDark = "default"
type PlotlyColorThemeLight = "default" | "seaborn" | "presentation" | "ggplot2"
type PlotlyColorTheme = {
dark: PlotlyColorThemeDark
light: PlotlyColorThemeLight
}
+198
View File
@@ -0,0 +1,198 @@
import * as Optuna from "@optuna/types"
export type PreferenceFeedbackMode = "ChooseWorst"
export type GraphVisibility = {
history: boolean
paretoFront: boolean
parallelCoordinate: boolean
intermediateValues: boolean
edf: boolean
contour: boolean
importances: boolean
slice: boolean
}
export type TrialParam = {
name: string
param_internal_value: number
param_external_value: string
param_external_type: string
distribution: Optuna.Distribution
}
export type ParamImportance = {
name: string
importance: number
distribution: Optuna.Distribution
}
export type SearchSpaceItem = {
name: string
distribution: Optuna.Distribution
}
export type Note = {
version: number
body: string
}
export type Artifact = {
artifact_id: string
filename: string
mimetype: string
encoding: string
}
export type Trial = {
trial_id: number
study_id: number
number: number
state: Optuna.TrialState
values?: number[]
intermediate_values: Optuna.TrialIntermediateValue[]
datetime_start?: Date
datetime_complete?: Date
params: TrialParam[]
fixed_params: {
name: string
param_external_value: string
}[]
user_attrs: Optuna.Attribute[]
constraints: number[]
note: Note
artifacts: Artifact[]
}
export type StudySummary = {
study_id: number
study_name: string
directions: Optuna.StudyDirection[]
user_attrs: Optuna.Attribute[]
is_preferential: boolean
datetime_start?: Date
}
export type ObjectiveChoiceWidget = {
type: "choice"
description: string
user_attr_key?: string
choices: string[]
values: number[]
}
export type ObjectiveSliderWidget = {
type: "slider"
description: string
user_attr_key?: string
min: number
max: number
step: number | null
labels:
| {
value: number
label: string
}[]
| null
}
export type ObjectiveTextInputWidget = {
type: "text"
description: string
optional: boolean
user_attr_key?: string
}
export type ObjectiveUserAttrRef = {
type: "user_attr"
key: string
}
export type ObjectiveFormWidget =
| ObjectiveChoiceWidget
| ObjectiveSliderWidget
| ObjectiveTextInputWidget
| ObjectiveUserAttrRef
export type UserAttrFormWidget =
| ObjectiveChoiceWidget
| ObjectiveSliderWidget
| ObjectiveTextInputWidget
export type FormWidgets =
| {
output_type: "objective"
widgets: ObjectiveFormWidget[]
}
| {
output_type: "user_attr"
widgets: UserAttrFormWidget[]
}
export type PlotlyGraphObject = {
id: string
graph_object: string
}
export type FeedbackComponentNote = {
output_type: "note"
}
export type FeedbackComponentArtifact = {
output_type: "artifact"
artifact_key: string
}
export type FeedbackComponentType =
| FeedbackComponentArtifact
| FeedbackComponentNote
export type StudyDetail = {
id: number
name: string
directions: Optuna.StudyDirection[]
user_attrs: Optuna.Attribute[]
datetime_start: Date
best_trials: Trial[]
trials: Trial[]
intersection_search_space: SearchSpaceItem[]
union_search_space: SearchSpaceItem[]
union_user_attrs: Optuna.AttributeSpec[]
has_intermediate_values: boolean
note: Note
is_preferential: boolean
objective_names?: string[]
form_widgets?: FormWidgets
feedback_component_type: FeedbackComponentType
preferences?: [number, number][]
preference_history?: PreferenceHistory[]
plotly_graph_objects: PlotlyGraphObject[]
artifacts: Artifact[]
skipped_trial_numbers: number[]
}
export type StudyDetails = {
[study_id: string]: StudyDetail
}
export type PreferenceHistory = {
id: string
candidates: number[]
clicked: number
feedback_mode: PreferenceFeedbackMode
timestamp: Date
preferences: [number, number][]
is_removed: boolean
}
export type PlotlyColorThemeDark = "default"
export type PlotlyColorThemeLight =
| "default"
| "seaborn"
| "presentation"
| "ggplot2"
export type PlotlyColorTheme = {
dark: PlotlyColorThemeDark
light: PlotlyColorThemeLight
}
+1 -1
View File
@@ -15,7 +15,7 @@
"module": "esnext",
"esModuleInterop": true,
"allowSyntheticDefaultImports": true,
"target": "es5",
"target": "es6",
"jsx": "react",
"sourceMap": true,
"strict": true
+3 -3
View File
@@ -7170,9 +7170,9 @@
}
},
"node_modules/electron-to-chromium": {
"version": "1.4.734",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.734.tgz",
"integrity": "sha512-pYfGUc+ll8AOzLbLC0lfgwkvCZIV+sKGuFFsSNuF3K3ujrmem8jIjg/t6DNq0J7biTSS1hCt/Hts0nmA3ZyprQ==",
"version": "1.4.735",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.735.tgz",
"integrity": "sha512-pkYpvwg8VyOTQAeBqZ7jsmpCjko1Qc6We1ZtZCjRyYbT5v4AIUKDy5cQTRotQlSSZmMr8jqpEt6JtOj5k7lR7A==",
"dev": true
},
"node_modules/emoji-regex": {
+1 -1
View File
@@ -14,7 +14,7 @@ export type IntDistribution = {
type: "IntDistribution"
low: number
high: number
step: number | null
step: number
log: boolean
}