mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-17 12:30:35 +08:00
Merge pull request #862 from porink0424/feat/separate-react-component
Separated UI components into `@optuna/react` (Continued from #851)
This commit is contained in:
Generated
+12
@@ -24,6 +24,7 @@
|
||||
"react-router-dom": "^6.22.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@optuna/types": "../tslib/types",
|
||||
"@types/plotly.js": "^2.29.2",
|
||||
"@types/react": "^18.2.64",
|
||||
"@types/react-dom": "^18.2.21",
|
||||
@@ -68,6 +69,7 @@
|
||||
"@storybook/react": "^8.0.4",
|
||||
"@storybook/react-vite": "^8.0.4",
|
||||
"@storybook/test": "^8.0.4",
|
||||
"@testing-library/jest-dom": "^6.4.2",
|
||||
"@testing-library/react": "^14.2.2",
|
||||
"@types/plotly.js-dist-min": "^2.3.4",
|
||||
"@types/react": "^18.2.55",
|
||||
@@ -91,6 +93,12 @@
|
||||
"@optuna/types": "../types/"
|
||||
}
|
||||
},
|
||||
"../tslib/types": {
|
||||
"name": "@optuna/types",
|
||||
"version": "0.0.1",
|
||||
"dev": true,
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/@ampproject/remapping": {
|
||||
"version": "2.3.0",
|
||||
"resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz",
|
||||
@@ -1338,6 +1346,10 @@
|
||||
"resolved": "../tslib/storage",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@optuna/types": {
|
||||
"resolved": "../tslib/types",
|
||||
"link": true
|
||||
},
|
||||
"node_modules/@popperjs/core": {
|
||||
"version": "2.11.8",
|
||||
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.11.8.tgz",
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
"build:vscode": "webpack"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@optuna/types": "../tslib/types",
|
||||
"@types/plotly.js": "^2.29.2",
|
||||
"@types/react": "^18.2.64",
|
||||
"@types/react-dom": "^18.2.21",
|
||||
|
||||
@@ -13,11 +13,16 @@ import {
|
||||
useTheme,
|
||||
} from "@mui/material"
|
||||
import Grid2 from "@mui/material/Unstable_Grid2"
|
||||
import { PlotHistory, TrialTable } from "@optuna/react"
|
||||
import {
|
||||
PlotHistory,
|
||||
PlotImportance,
|
||||
PlotIntermediateValues,
|
||||
TrialTable,
|
||||
} from "@optuna/react"
|
||||
import * as Optuna from "@optuna/types"
|
||||
import init, { wasm_fanova_calculate } from "optuna"
|
||||
import React, { FC, useContext, useState, useEffect } from "react"
|
||||
import { Link, useParams } from "react-router-dom"
|
||||
import { PlotImportance } from "./PlotImportance"
|
||||
import { PlotIntermediateValues } from "./PlotIntermediateValues"
|
||||
import { StorageContext } from "./StorageProvider"
|
||||
|
||||
export const StudyDetail: FC<{
|
||||
@@ -40,6 +45,68 @@ export const StudyDetail: FC<{
|
||||
fetchStudy()
|
||||
}, [storage, idxNumber])
|
||||
|
||||
const [importance, setImportance] = useState<Optuna.ParamImportance[][]>([])
|
||||
const filterFunc = (trial: Optuna.Trial, objectiveId: number): boolean => {
|
||||
if (trial.state !== "Complete" && trial.state !== "Pruned") {
|
||||
return false
|
||||
}
|
||||
if (trial.values === undefined) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
trial.values.length > objectiveId &&
|
||||
trial.values[objectiveId] !== Infinity &&
|
||||
trial.values[objectiveId] !== -Infinity
|
||||
)
|
||||
}
|
||||
// biome-ignore lint/correctness/useExhaustiveDependencies: <explanation>
|
||||
useEffect(() => {
|
||||
async function run_wasm() {
|
||||
if (study === null) {
|
||||
return
|
||||
}
|
||||
|
||||
await init()
|
||||
|
||||
const x: Optuna.ParamImportance[][] = study.directions.map(
|
||||
(_d, objectiveId) => {
|
||||
const filteredTrials = study.trials.filter((t) =>
|
||||
filterFunc(t, objectiveId)
|
||||
)
|
||||
if (filteredTrials.length === 0) {
|
||||
return study.union_search_space.map((s) => {
|
||||
return {
|
||||
name: s.name,
|
||||
importance: 0.5,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const features = study.intersection_search_space.map((s) =>
|
||||
filteredTrials
|
||||
.map(
|
||||
(t) =>
|
||||
t.params.find((p) => p.name === s.name) as Optuna.TrialParam
|
||||
)
|
||||
.map((p) => p.param_internal_value)
|
||||
)
|
||||
const values = filteredTrials.map(
|
||||
(t) => t.values?.[objectiveId] as number
|
||||
)
|
||||
// TODO: handle errors thrown by wasm_fanova_calculate
|
||||
const importance = wasm_fanova_calculate(features, values)
|
||||
return study.intersection_search_space.map((s, i) => ({
|
||||
name: s.name,
|
||||
importance: importance[i],
|
||||
}))
|
||||
}
|
||||
)
|
||||
setImportance(x)
|
||||
}
|
||||
|
||||
run_wasm()
|
||||
}, [study])
|
||||
|
||||
return (
|
||||
<>
|
||||
<AppBar position="static">
|
||||
@@ -113,7 +180,9 @@ export const StudyDetail: FC<{
|
||||
<Grid2 xs={6}>
|
||||
<Card sx={{ margin: theme.spacing(2) }}>
|
||||
<CardContent>
|
||||
{!!study && <PlotImportance study={study} />}
|
||||
{!!study && (
|
||||
<PlotImportance study={study} importance={importance} />
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</Grid2>
|
||||
|
||||
@@ -24,3 +24,6 @@ types
|
||||
|
||||
# sample db
|
||||
!public/sample_db.sqlite3
|
||||
|
||||
# test assets
|
||||
test/asset
|
||||
Generated
+494
-947
File diff suppressed because it is too large
Load Diff
@@ -28,5 +28,15 @@ const useMockStudies = () => {
|
||||
|
||||
export const useMockStudy = (studyId: number | undefined) => {
|
||||
const mockStudies = useMockStudies()
|
||||
return mockStudies.find((study) => study.study_id === studyId)
|
||||
const mockStudy = mockStudies.find((study) => study.study_id === studyId)
|
||||
const mockImportance: Optuna.ParamImportance[][] = [
|
||||
[
|
||||
{ name: "dropout_l0", importance: 0.07990265450296263 },
|
||||
{ name: "lr", importance: 0.07328545409147895 },
|
||||
{ name: "n_layers", importance: 0.028260844392780343 },
|
||||
{ name: "n_units_l0", importance: 0.1600821009129565 },
|
||||
{ name: "optimizer", importance: 0.3296378694329876 },
|
||||
],
|
||||
]
|
||||
return { study: mockStudy, importance: mockImportance }
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { lightTheme } from "../styles/lightTheme"
|
||||
import { PlotHistory } from "./PlotHistory"
|
||||
|
||||
const meta: Meta<typeof PlotHistory> = {
|
||||
@@ -8,14 +11,17 @@ const meta: Meta<typeof PlotHistory> = {
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const study = useMockStudy(storyContext.parameters?.studyId)
|
||||
const { study } = useMockStudy(storyContext.parameters?.studyId)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
}}
|
||||
/>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
},
|
||||
],
|
||||
@@ -24,7 +30,7 @@ const meta: Meta<typeof PlotHistory> = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof PlotHistory>
|
||||
|
||||
export const MockStudy1: Story = {
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ThemeProvider } from "@mui/material"
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { darkTheme } from "../styles/darkTheme"
|
||||
import { PlotHistory } from "./PlotHistory"
|
||||
@@ -10,10 +11,11 @@ const meta: Meta<typeof PlotHistory> = {
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const study = useMockStudy(storyContext.parameters?.studyId)
|
||||
const { study } = useMockStudy(storyContext.parameters?.studyId)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
@@ -31,7 +33,7 @@ const meta: Meta<typeof PlotHistory> = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof PlotHistory>
|
||||
|
||||
export const MockStudy1: Story = {
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { lightTheme } from "../styles/lightTheme"
|
||||
import { PlotImportance } from "./PlotImportance"
|
||||
|
||||
const meta: Meta<typeof PlotImportance> = {
|
||||
component: PlotImportance,
|
||||
title: "PlotImportance",
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const { study, importance } = useMockStudy(
|
||||
storyContext.parameters?.studyId
|
||||
)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
importance,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof PlotImportance>
|
||||
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
}
|
||||
+9
-59
@@ -1,55 +1,19 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material"
|
||||
import { plotlyDarkTemplate } from "@optuna/react"
|
||||
import init, { wasm_fanova_calculate } from "optuna"
|
||||
import * as Optuna from "@optuna/types"
|
||||
import * as plotly from "plotly.js-dist-min"
|
||||
import React, { FC, useEffect, useState } from "react"
|
||||
import { FC, useEffect } from "react"
|
||||
import { plotlyDarkTemplate } from "./PlotlyDarkMode"
|
||||
|
||||
const plotDomId = "graph-hyperparameter-importances"
|
||||
|
||||
export const PlotImportance: FC<{ study: Study }> = ({ study }) => {
|
||||
export const PlotImportance: FC<{
|
||||
study: Optuna.Study
|
||||
importance: Optuna.ParamImportance[][]
|
||||
}> = ({ study, importance }) => {
|
||||
const theme = useTheme()
|
||||
const objectiveNames: string[] = study.directions.map(
|
||||
(d, i) => `Objective ${i}`
|
||||
(_d, i) => `Objective ${i}`
|
||||
)
|
||||
const [importance, setImportance] = useState<ParamImportance[][]>([])
|
||||
|
||||
useEffect(() => {
|
||||
async function run_wasm() {
|
||||
await init()
|
||||
|
||||
const x: ParamImportance[][] = study.directions.map((d, objectiveId) => {
|
||||
const filteredTrials = study.trials.filter((t) =>
|
||||
filterFunc(t, objectiveId)
|
||||
)
|
||||
if (filteredTrials.length === 0) {
|
||||
return study.union_search_space.map((s) => {
|
||||
return {
|
||||
name: s.name,
|
||||
importance: 0.5,
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const features = study.intersection_search_space.map((s) =>
|
||||
filteredTrials
|
||||
.map((t) => t.params.find((p) => p.name === s.name) as TrialParam)
|
||||
.map((p) => p.param_internal_value)
|
||||
)
|
||||
const values = filteredTrials.map(
|
||||
(t) => t.values?.[objectiveId] as number
|
||||
)
|
||||
// TODO: handle errors thrown by wasm_fanova_calculate
|
||||
const importance = wasm_fanova_calculate(features, values)
|
||||
return study.intersection_search_space.map((s, i) => ({
|
||||
name: s.name,
|
||||
importance: importance[i],
|
||||
}))
|
||||
})
|
||||
setImportance(x)
|
||||
}
|
||||
|
||||
run_wasm()
|
||||
}, [study])
|
||||
|
||||
useEffect(() => {
|
||||
if (importance.length > 0) {
|
||||
@@ -70,22 +34,8 @@ export const PlotImportance: FC<{ study: Study }> = ({ study }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const filterFunc = (trial: Trial, objectiveId: number): boolean => {
|
||||
if (trial.state !== "Complete" && trial.state !== "Pruned") {
|
||||
return false
|
||||
}
|
||||
if (trial.values === undefined) {
|
||||
return false
|
||||
}
|
||||
return (
|
||||
trial.values.length > objectiveId &&
|
||||
trial.values[objectiveId] !== Infinity &&
|
||||
trial.values[objectiveId] !== -Infinity
|
||||
)
|
||||
}
|
||||
|
||||
const plotParamImportancesBeta = (
|
||||
importances: ParamImportance[][],
|
||||
importances: Optuna.ParamImportance[][],
|
||||
objectiveNames: string[],
|
||||
mode: string
|
||||
) => {
|
||||
@@ -0,0 +1,43 @@
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { darkTheme } from "../styles/darkTheme"
|
||||
import { PlotImportance } from "./PlotImportance"
|
||||
|
||||
const meta: Meta<typeof PlotImportance> = {
|
||||
component: PlotImportance,
|
||||
title: "PlotImportanceDark",
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const { study, importance } = useMockStudy(
|
||||
storyContext.parameters?.studyId
|
||||
)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
importance,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
},
|
||||
],
|
||||
parameters: {
|
||||
backgrounds: { default: "dark" },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof PlotImportance>
|
||||
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { lightTheme } from "../styles/lightTheme"
|
||||
import { PlotIntermediateValues } from "./PlotIntermediateValues"
|
||||
|
||||
const meta: Meta<typeof PlotIntermediateValues> = {
|
||||
component: PlotIntermediateValues,
|
||||
title: "PlotIntermediateValues",
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const { study } = useMockStudy(storyContext.parameters?.studyId)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
trials: study.trials,
|
||||
includePruned: false,
|
||||
logScale: false,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof PlotIntermediateValues>
|
||||
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
}
|
||||
+5
-4
@@ -1,12 +1,13 @@
|
||||
import { Box, Typography, useTheme } from "@mui/material"
|
||||
import { plotlyDarkTemplate } from "@optuna/react"
|
||||
import * as Optuna from "@optuna/types"
|
||||
import * as plotly from "plotly.js-dist-min"
|
||||
import React, { FC, useEffect } from "react"
|
||||
import { FC, useEffect } from "react"
|
||||
import { plotlyDarkTemplate } from "./PlotlyDarkMode"
|
||||
|
||||
const plotDomId = "graph-intermediate-values"
|
||||
|
||||
export const PlotIntermediateValues: FC<{
|
||||
trials: Trial[]
|
||||
trials: Optuna.Trial[]
|
||||
includePruned: boolean
|
||||
logScale: boolean
|
||||
}> = ({ trials, includePruned, logScale }) => {
|
||||
@@ -36,7 +37,7 @@ export const PlotIntermediateValues: FC<{
|
||||
}
|
||||
|
||||
const plotIntermediateValue = (
|
||||
trials: Trial[],
|
||||
trials: Optuna.Trial[],
|
||||
mode: string,
|
||||
filterCompleteTrial: boolean,
|
||||
filterPrunedTrial: boolean,
|
||||
@@ -0,0 +1,42 @@
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { darkTheme } from "../styles/darkTheme"
|
||||
import { PlotIntermediateValues } from "./PlotIntermediateValues"
|
||||
|
||||
const meta: Meta<typeof PlotIntermediateValues> = {
|
||||
component: PlotIntermediateValues,
|
||||
title: "PlotIntermediateValuesDark",
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const { study } = useMockStudy(storyContext.parameters?.studyId)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
trials: study.trials,
|
||||
includePruned: false,
|
||||
logScale: false,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
},
|
||||
],
|
||||
parameters: {
|
||||
backgrounds: { default: "dark" },
|
||||
},
|
||||
}
|
||||
|
||||
export default meta
|
||||
type Story = StoryObj<typeof PlotIntermediateValues>
|
||||
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
}
|
||||
@@ -1,5 +1,8 @@
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { lightTheme } from "../styles/lightTheme"
|
||||
import { TrialTable } from "./TrialTable"
|
||||
|
||||
const meta: Meta<typeof TrialTable> = {
|
||||
@@ -8,14 +11,17 @@ const meta: Meta<typeof TrialTable> = {
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const study = useMockStudy(storyContext.parameters?.studyId)
|
||||
const { study } = useMockStudy(storyContext.parameters?.studyId)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
}}
|
||||
/>
|
||||
<ThemeProvider theme={lightTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
}}
|
||||
/>
|
||||
</ThemeProvider>
|
||||
)
|
||||
},
|
||||
],
|
||||
@@ -24,7 +30,7 @@ const meta: Meta<typeof TrialTable> = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof TrialTable>
|
||||
|
||||
export const MockStudy1: Story = {
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ThemeProvider } from "@mui/material"
|
||||
import { CssBaseline, ThemeProvider } from "@mui/material"
|
||||
import { Meta, StoryObj } from "@storybook/react"
|
||||
import React from "react"
|
||||
import { useMockStudy } from "../MockStudies"
|
||||
import { darkTheme } from "../styles/darkTheme"
|
||||
import { TrialTable } from "./TrialTable"
|
||||
@@ -10,10 +11,11 @@ const meta: Meta<typeof TrialTable> = {
|
||||
tags: ["autodocs"],
|
||||
decorators: [
|
||||
(Story, storyContext) => {
|
||||
const study = useMockStudy(storyContext.parameters?.studyId)
|
||||
const { study } = useMockStudy(storyContext.parameters?.studyId)
|
||||
if (!study) return <p>loading...</p>
|
||||
return (
|
||||
<ThemeProvider theme={darkTheme}>
|
||||
<CssBaseline />
|
||||
<Story
|
||||
args={{
|
||||
study,
|
||||
@@ -31,7 +33,7 @@ const meta: Meta<typeof TrialTable> = {
|
||||
export default meta
|
||||
type Story = StoryObj<typeof TrialTable>
|
||||
|
||||
export const MockStudy1: Story = {
|
||||
export const MockStudyExample1: Story = {
|
||||
parameters: {
|
||||
studyId: 1,
|
||||
},
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
export { DataGrid } from "./components/DataGrid"
|
||||
export type { DataGridColumn } from "./components/DataGrid"
|
||||
export { PlotHistory } from "./components/PlotHistory"
|
||||
export { TrialTable } from "./components/TrialTable"
|
||||
export { plotlyDarkTemplate } from "./components/PlotlyDarkMode"
|
||||
export { PlotHistory } from "./components/PlotHistory"
|
||||
export { PlotImportance } from "./components/PlotImportance"
|
||||
export { PlotIntermediateValues } from "./components/PlotIntermediateValues"
|
||||
export { TrialTable } from "./components/TrialTable"
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
import { createTheme } from "@mui/material"
|
||||
import blue from "@mui/material/colors/blue"
|
||||
import pink from "@mui/material/colors/pink"
|
||||
|
||||
export const darkTheme = createTheme({
|
||||
palette: {
|
||||
mode: "dark",
|
||||
primary: blue,
|
||||
secondary: pink,
|
||||
},
|
||||
})
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
import { createTheme } from "@mui/material"
|
||||
import blue from "@mui/material/colors/blue"
|
||||
import pink from "@mui/material/colors/pink"
|
||||
|
||||
export const lightTheme = createTheme({
|
||||
palette: {
|
||||
mode: "light",
|
||||
primary: blue,
|
||||
secondary: pink,
|
||||
},
|
||||
})
|
||||
@@ -0,0 +1,44 @@
|
||||
import * as Optuna from "@optuna/types"
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import React from "react"
|
||||
import { describe, expect, test } from "vitest"
|
||||
import { PlotImportance } from "../src/components/PlotImportance"
|
||||
|
||||
describe("PlotImportance Tests", async () => {
|
||||
const setup = ({
|
||||
study,
|
||||
importance,
|
||||
dataTestId,
|
||||
}: {
|
||||
study: Optuna.Study
|
||||
importance: Optuna.ParamImportance[][]
|
||||
dataTestId: string
|
||||
}) => {
|
||||
const Wrapper = ({
|
||||
dataTestId,
|
||||
children,
|
||||
}: {
|
||||
dataTestId: string
|
||||
children: React.ReactNode
|
||||
}) => <div data-testid={dataTestId}>{children}</div>
|
||||
return render(
|
||||
<Wrapper dataTestId={dataTestId}>
|
||||
<PlotImportance study={study} importance={importance} />
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
for (const study of window.mockStudies) {
|
||||
test(`PlotImportance (study name: ${study.study_name})`, () => {
|
||||
const importance = window.mockImportances[study.study_name] ?? []
|
||||
setup({
|
||||
study,
|
||||
importance,
|
||||
dataTestId: `plot-importance-${study.study_id}`,
|
||||
})
|
||||
expect(
|
||||
screen.getByTestId(`plot-importance-${study.study_id}`)
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -0,0 +1,38 @@
|
||||
import * as Optuna from "@optuna/types"
|
||||
import { render, screen } from "@testing-library/react"
|
||||
import React from "react"
|
||||
import { describe, expect, test } from "vitest"
|
||||
import { PlotIntermediateValues } from "../src/components/PlotIntermediateValues"
|
||||
|
||||
describe("PlotIntermediateValues Tests", async () => {
|
||||
const setup = ({
|
||||
study,
|
||||
dataTestId,
|
||||
}: { study: Optuna.Study; dataTestId: string }) => {
|
||||
const Wrapper = ({
|
||||
dataTestId,
|
||||
children,
|
||||
}: {
|
||||
dataTestId: string
|
||||
children: React.ReactNode
|
||||
}) => <div data-testid={dataTestId}>{children}</div>
|
||||
return render(
|
||||
<Wrapper dataTestId={dataTestId}>
|
||||
<PlotIntermediateValues
|
||||
trials={study.trials}
|
||||
includePruned={false}
|
||||
logScale={false}
|
||||
/>
|
||||
</Wrapper>
|
||||
)
|
||||
}
|
||||
|
||||
for (const study of window.mockStudies) {
|
||||
test(`PlotIntermediateValues (study name: ${study.study_name})`, () => {
|
||||
setup({ study, dataTestId: `plot-intermediatevalues-${study.study_id}` })
|
||||
expect(
|
||||
screen.getByTestId(`plot-intermediatevalues-${study.study_id}`)
|
||||
).toBeInTheDocument()
|
||||
})
|
||||
}
|
||||
})
|
||||
@@ -1,3 +1,4 @@
|
||||
import json
|
||||
import logging
|
||||
import math
|
||||
import os.path
|
||||
@@ -7,6 +8,8 @@ from typing import Tuple
|
||||
import optuna
|
||||
from optuna.distributions import CategoricalDistribution
|
||||
from optuna.distributions import FloatDistribution
|
||||
from optuna.importance import get_param_importances
|
||||
from optuna.importance import PedAnovaImportanceEvaluator
|
||||
from optuna.storages import BaseStorage
|
||||
from optuna.storages import JournalFileStorage
|
||||
from optuna.storages import JournalStorage
|
||||
@@ -22,7 +25,9 @@ def remove_assets() -> None:
|
||||
os.mkdir(BASE_DIR)
|
||||
|
||||
|
||||
def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStorage:
|
||||
def create_optuna_storage(
|
||||
storage: BaseStorage, params_importances: dict[str, list[dict[str, float]]]
|
||||
) -> optuna.storages.InMemoryStorage:
|
||||
# Single-objective study
|
||||
study = optuna.create_study(
|
||||
study_name="single-objective", storage=storage, sampler=optuna.samplers.RandomSampler()
|
||||
@@ -35,6 +40,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return (x1 - 2) ** 2 + (x2 - 5) ** 2
|
||||
|
||||
study.optimize(objective_single, n_trials=100)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single-objective study with dynamic search space
|
||||
study = optuna.create_study(
|
||||
@@ -49,6 +62,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return -((trial.suggest_float("x2", -10, 0) + 5) ** 2)
|
||||
|
||||
study.optimize(objective_single_dynamic, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
study = optuna.create_study(
|
||||
study_name="check-rank-plot", storage=storage, sampler=optuna.samplers.RandomSampler()
|
||||
@@ -64,6 +85,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return (x1 - 2) ** 2 + (x2 - 5) ** 2
|
||||
|
||||
study.optimize(objective_single, n_trials=1000)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single-objective study
|
||||
study = optuna.create_study(study_name="single-objective-user-attrs", storage=storage)
|
||||
@@ -79,6 +108,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return (x1 - 2) ** 2 + (x2 - 5) ** 2
|
||||
|
||||
study.optimize(objective_single_user_attr, n_trials=100)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single objective study with 'inf', '-inf', or 'nan' value
|
||||
study = optuna.create_study(study_name="single-inf", storage=storage)
|
||||
@@ -93,6 +130,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return x**2
|
||||
|
||||
study.optimize(objective_single_inf, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single objective pruned after reported 'inf', '-inf', or 'nan'
|
||||
study = optuna.create_study(study_name="single-inf-report", storage=storage)
|
||||
@@ -112,6 +157,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return x**2
|
||||
|
||||
study.optimize(objective_single_inf_report, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single objective with reported nan value
|
||||
study = optuna.create_study(study_name="single-nan-report", storage=storage)
|
||||
@@ -124,6 +177,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return (x1 - 2) ** 2 + (x2 - 5) ** 2
|
||||
|
||||
study.optimize(objective_single_nan_report, n_trials=100)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single-objective study with 1 parameter
|
||||
study = optuna.create_study(
|
||||
@@ -135,6 +196,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return -((x1 - 2) ** 2)
|
||||
|
||||
study.optimize(objective_single_with_1param, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Single-objective study with 1 parameter
|
||||
study = optuna.create_study(study_name="long-parameter-names", storage=storage)
|
||||
@@ -149,6 +218,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return (x1 - 2) ** 2 + (x2 - 5) ** 2
|
||||
|
||||
study.optimize(objective_long_parameter_names, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Multi-objective study
|
||||
study = optuna.create_study(
|
||||
@@ -166,6 +243,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return v0, v1
|
||||
|
||||
study.optimize(objective_multi, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Multi-objective study with dynamic search space
|
||||
study = optuna.create_study(
|
||||
@@ -188,6 +273,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return v0, v1
|
||||
|
||||
study.optimize(objective_multi_dynamic, n_trials=50)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Pruning with no intermediate values
|
||||
study = optuna.create_study(study_name="binh-korn-function-with-constraints", storage=storage)
|
||||
@@ -201,6 +294,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return v
|
||||
|
||||
study.optimize(objective_prune_with_no_trials, n_trials=100)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# With failed trials
|
||||
study = optuna.create_study(study_name="failed trials", storage=storage)
|
||||
@@ -214,6 +315,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return v
|
||||
|
||||
study.optimize(objective_sometimes_got_failed, n_trials=100, catch=(Exception,))
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# No trials single-objective study
|
||||
study = optuna.create_study(study_name="no trials single-objective study", storage=storage)
|
||||
@@ -251,6 +360,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return v0
|
||||
|
||||
study.optimize(objective_constraints, n_trials=100)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
|
||||
# Study with Running Trials
|
||||
study = optuna.create_study(
|
||||
@@ -284,6 +401,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return 0.0
|
||||
|
||||
study.optimize(objective_intermediate_values, n_trials=10)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
trial = study.ask(
|
||||
{"x": FloatDistribution(0, 10), "y": CategoricalDistribution(["Foo", "Bar"])}
|
||||
) # To create a running trial
|
||||
@@ -308,6 +433,14 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
return 0.0
|
||||
|
||||
study.optimize(objective_intermediate_values_constraints, n_trials=10)
|
||||
params_importances[study.study_name] = [
|
||||
get_param_importances(
|
||||
study,
|
||||
target=lambda trial: trial.values[objective_id],
|
||||
evaluator=PedAnovaImportanceEvaluator(),
|
||||
)
|
||||
for objective_id in range(len(study.directions))
|
||||
]
|
||||
trial = study.ask(
|
||||
{"x": FloatDistribution(0, 10), "y": CategoricalDistribution(["Foo", "Bar"])}
|
||||
) # To create a running trial
|
||||
@@ -318,7 +451,11 @@ def create_optuna_storage(storage: BaseStorage) -> optuna.storages.InMemoryStora
|
||||
def main() -> None:
|
||||
remove_assets()
|
||||
storage = JournalStorage(JournalFileStorage(os.path.join(BASE_DIR, "journal.log")))
|
||||
create_optuna_storage(storage)
|
||||
params_importances: dict[str, dict[str, float]] = {}
|
||||
create_optuna_storage(storage, params_importances)
|
||||
|
||||
with open(os.path.join(BASE_DIR, "params_importances.json"), "w") as f:
|
||||
json.dump(params_importances, f, indent=2)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -5,14 +5,15 @@ import { loadStorageFromFile } from "../src/utils/loadStorageFromFile"
|
||||
declare global {
|
||||
interface Window {
|
||||
mockStudies: Optuna.Study[]
|
||||
mockImportances: Record<string, Optuna.ParamImportance[][]>
|
||||
}
|
||||
}
|
||||
|
||||
const data = fs.readFileSync("./test/asset/journal.log")
|
||||
const blob = new Blob([data])
|
||||
const file = new File([blob], "journal.log")
|
||||
const journalData = fs.readFileSync("./test/asset/journal.log")
|
||||
const journalBlob = new Blob([journalData])
|
||||
const journalFile = new File([journalBlob], "journal.log")
|
||||
const mockStudies: Optuna.Study[] = []
|
||||
await loadStorageFromFile(file, (value) => {
|
||||
await loadStorageFromFile(journalFile, (value) => {
|
||||
if (Array.isArray(value)) {
|
||||
mockStudies.push(...value)
|
||||
} else {
|
||||
@@ -21,6 +22,22 @@ await loadStorageFromFile(file, (value) => {
|
||||
})
|
||||
window.mockStudies = mockStudies
|
||||
|
||||
const importancesData = fs.readFileSync("./test/asset/params_importances.json")
|
||||
const importancesJson = JSON.parse(importancesData.toString())
|
||||
const mockImportances: Record<string, Optuna.ParamImportance[][]> = {}
|
||||
for (const key in importancesJson) {
|
||||
mockImportances[key] = importancesJson[key].map(
|
||||
(importance: Record<string, number>) => {
|
||||
const importanceArray: Optuna.ParamImportance[] = []
|
||||
for (const name in importance) {
|
||||
importanceArray.push({ name, importance: importance[name] })
|
||||
}
|
||||
return importanceArray
|
||||
}
|
||||
)
|
||||
}
|
||||
window.mockImportances = mockImportances
|
||||
|
||||
// mock window.URL.createObjectURL in JSDOM
|
||||
window.HTMLCanvasElement.prototype.getContext = () => null
|
||||
window.URL.createObjectURL = () => ""
|
||||
|
||||
Reference in New Issue
Block a user