diff --git a/biome.json b/biome.json index fa75000f..c30b7bc9 100644 --- a/biome.json +++ b/biome.json @@ -14,6 +14,7 @@ ], "ignore": [ "optuna_dashboard/ts/components/PlotlyColorTemplates.ts", + "tslib/react/src/components/PlotlyDarkMode.ts", "standalone_app/src/PlotlyDarkMode.ts", "tslib/**/pkg/*" ] diff --git a/standalone_app/package-lock.json b/standalone_app/package-lock.json index 802843c0..98b3c5e5 100644 --- a/standalone_app/package-lock.json +++ b/standalone_app/package-lock.json @@ -57,7 +57,6 @@ "recoil": "^0.7.7" }, "devDependencies": { - "@biomejs/biome": "1.5.3", "@optuna/types": "../types/", "@storybook/addon-essentials": "^8.0.4", "@storybook/addon-interactions": "^8.0.4", @@ -1936,47 +1935,6 @@ "dev": true, "license": "BSD-2-Clause" }, - "../tslib/react/node_modules/@biomejs/biome": { - "version": "1.5.3", - "dev": true, - "hasInstallScript": true, - "license": "MIT OR Apache-2.0", - "bin": { - "biome": "bin/biome" - }, - "engines": { - "node": ">=14.*" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/biome" - }, - "optionalDependencies": { - "@biomejs/cli-darwin-arm64": "1.5.3", - "@biomejs/cli-darwin-x64": "1.5.3", - "@biomejs/cli-linux-arm64": "1.5.3", - "@biomejs/cli-linux-arm64-musl": "1.5.3", - "@biomejs/cli-linux-x64": "1.5.3", - "@biomejs/cli-linux-x64-musl": "1.5.3", - "@biomejs/cli-win32-arm64": "1.5.3", - "@biomejs/cli-win32-x64": "1.5.3" - } - }, - "../tslib/react/node_modules/@biomejs/cli-darwin-x64": { - "version": "1.5.3", - "cpu": [ - "x64" - ], - "dev": true, - "license": "MIT OR Apache-2.0", - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">=14.*" - } - }, "../tslib/react/node_modules/@colors/colors": { "version": "1.5.0", "dev": true, diff --git a/standalone_app/src/components/StudyDetail.tsx b/standalone_app/src/components/StudyDetail.tsx index d39dc11d..866c477e 100644 --- a/standalone_app/src/components/StudyDetail.tsx +++ b/standalone_app/src/components/StudyDetail.tsx @@ -13,12 +13,12 @@ import { useTheme, } from "@mui/material" import Grid2 from "@mui/material/Unstable_Grid2" +import { PlotHistory, TrialTable } from "@optuna/react" 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" -import { PlotHistory, TrialTable } from "@optuna/react" export const StudyDetail: FC<{ toggleColorMode: () => void diff --git a/tslib/react/.storybook/main.ts b/tslib/react/.storybook/main.ts index 895d2127..0c4a305a 100644 --- a/tslib/react/.storybook/main.ts +++ b/tslib/react/.storybook/main.ts @@ -1,4 +1,4 @@ -import type { StorybookConfig } from "@storybook/react-vite"; +import type { StorybookConfig } from "@storybook/react-vite" const config: StorybookConfig = { stories: ["../src/**/*.stories.@(js|jsx|mjs|ts|tsx)"], @@ -15,7 +15,7 @@ const config: StorybookConfig = { autodocs: "tag", }, async viteFinal(config) { - const { mergeConfig } = await import("vite"); + const { mergeConfig } = await import("vite") return mergeConfig(config, { server: { fs: { @@ -23,7 +23,7 @@ const config: StorybookConfig = { allow: [...(config.server?.fs?.allow ?? []), ".."], }, }, - }); + }) }, -}; -export default config; +} +export default config diff --git a/tslib/react/.storybook/preview.ts b/tslib/react/.storybook/preview.ts index 37914b18..62a4ea33 100644 --- a/tslib/react/.storybook/preview.ts +++ b/tslib/react/.storybook/preview.ts @@ -1,4 +1,4 @@ -import type { Preview } from "@storybook/react"; +import type { Preview } from "@storybook/react" const preview: Preview = { parameters: { @@ -9,6 +9,6 @@ const preview: Preview = { }, }, }, -}; +} -export default preview; +export default preview diff --git a/tslib/react/biome.json b/tslib/react/biome.json deleted file mode 100644 index 81261bda..00000000 --- a/tslib/react/biome.json +++ /dev/null @@ -1,26 +0,0 @@ -{ - "$schema": "https://biomejs.dev/schemas/1.5.3/schema.json", - "organizeImports": { - "enabled": true - }, - "files": { - "ignore": ["node_modules", "src/components/PlotlyDarkMode.ts", "pkg"] - }, - "linter": { - "enabled": true, - "rules": { - "recommended": true, - "complexity": { - "noForEach": "off" - }, - "style": { - "noUselessElse": "off" - } - } - }, - "formatter": { - "enabled": true, - "lineWidth": 80, - "indentStyle": "space" - } -} diff --git a/tslib/react/package-lock.json b/tslib/react/package-lock.json index 3051729a..46857a11 100644 --- a/tslib/react/package-lock.json +++ b/tslib/react/package-lock.json @@ -21,7 +21,6 @@ "recoil": "^0.7.7" }, "devDependencies": { - "@biomejs/biome": "1.5.3", "@optuna/types": "../types/", "@storybook/addon-essentials": "^8.0.4", "@storybook/addon-interactions": "^8.0.4", diff --git a/tslib/react/package.json b/tslib/react/package.json index f7fad2b1..8d69972b 100644 --- a/tslib/react/package.json +++ b/tslib/react/package.json @@ -8,8 +8,6 @@ "types": "types/index.d.ts", "scripts": { "build": "tsc -d", - "lint": "biome lint .", - "fmt": "biome format --write .", "storybook": "storybook dev -p 6006" }, "files": [ @@ -39,7 +37,6 @@ "recoil": "^0.7.7" }, "devDependencies": { - "@biomejs/biome": "1.5.3", "@optuna/types": "../types/", "@storybook/addon-essentials": "^8.0.4", "@storybook/addon-interactions": "^8.0.4", diff --git a/tslib/react/src/MockStudies.ts b/tslib/react/src/MockStudies.ts index 3bd9a300..49b86111 100644 --- a/tslib/react/src/MockStudies.ts +++ b/tslib/react/src/MockStudies.ts @@ -1,32 +1,32 @@ -import * as Optuna from "@optuna/types"; -import { useEffect, useState } from "react"; -import { loadStorageFromFile } from "./utils/loadStorageFromFile"; +import * as Optuna from "@optuna/types" +import { useEffect, useState } from "react" +import { loadStorageFromFile } from "./utils/loadStorageFromFile" const fetchMockStudies = async () => { - const filePath = "sample_db.sqlite3"; - const res = await fetch(filePath); - const blob = await res.blob(); - const file = new File([blob], filePath); - const mockStudies: Optuna.Study[] = []; + const filePath = "sample_db.sqlite3" + const res = await fetch(filePath) + const blob = await res.blob() + const file = new File([blob], filePath) + const mockStudies: Optuna.Study[] = [] await loadStorageFromFile(file, (value) => { if (Array.isArray(value)) { - mockStudies.push(...value); + mockStudies.push(...value) } else { - mockStudies.push(...value([])); + mockStudies.push(...value([])) } - }); - return mockStudies; -}; + }) + return mockStudies +} const useMockStudies = () => { - const [mockStudies, setMockStudies] = useState([]); + const [mockStudies, setMockStudies] = useState([]) useEffect(() => { - fetchMockStudies().then((studies) => setMockStudies(studies)); - }, []); - return mockStudies; -}; + fetchMockStudies().then((studies) => setMockStudies(studies)) + }, []) + return mockStudies +} export const useMockStudy = (studyId: number | undefined) => { - const mockStudies = useMockStudies(); - return mockStudies.find((study) => study.study_id === studyId); -}; + const mockStudies = useMockStudies() + return mockStudies.find((study) => study.study_id === studyId) +} diff --git a/tslib/react/src/components/DataGrid.tsx b/tslib/react/src/components/DataGrid.tsx index 72918b9d..25509683 100644 --- a/tslib/react/src/components/DataGrid.tsx +++ b/tslib/react/src/components/DataGrid.tsx @@ -1,6 +1,6 @@ -import { Clear } from "@mui/icons-material"; -import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown"; -import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp"; +import { Clear } from "@mui/icons-material" +import KeyboardArrowDownIcon from "@mui/icons-material/KeyboardArrowDown" +import KeyboardArrowUpIcon from "@mui/icons-material/KeyboardArrowUp" import { Collapse, IconButton, @@ -13,128 +13,128 @@ import { TableRow, TableSortLabel, useTheme, -} from "@mui/material"; -import { styled } from "@mui/system"; -import React from "react"; +} from "@mui/material" +import { styled } from "@mui/system" +import React from "react" -type Order = "asc" | "desc"; +type Order = "asc" | "desc" // biome-ignore lint/suspicious/noExplicitAny: -type Value = any; +type Value = any -const defaultRowsPerPageOption = [10, 50, 100, { label: "All", value: -1 }]; +const defaultRowsPerPageOption = [10, 50, 100, { label: "All", value: -1 }] interface DataGridColumn { - field: keyof T; - label: string; - sortable?: boolean; - less?: (a: T, b: T, ascending: boolean) => number; - filterable?: boolean; - toCellValue?: (rowIndex: number) => string | React.ReactNode; - padding?: "normal" | "checkbox" | "none"; + field: keyof T + label: string + sortable?: boolean + less?: (a: T, b: T, ascending: boolean) => number + filterable?: boolean + toCellValue?: (rowIndex: number) => string | React.ReactNode + padding?: "normal" | "checkbox" | "none" } interface RowFilter { - columnIdx: number; - value: Value; + columnIdx: number + value: Value } function DataGrid(props: { - columns: DataGridColumn[]; - rows: T[]; - keyField: keyof T; - dense?: boolean; - collapseBody?: (rowIndex: number) => React.ReactNode; - initialRowsPerPage?: number; - rowsPerPageOption?: Array; - defaultFilter?: (row: T) => boolean; + columns: DataGridColumn[] + rows: T[] + keyField: keyof T + dense?: boolean + collapseBody?: (rowIndex: number) => React.ReactNode + initialRowsPerPage?: number + rowsPerPageOption?: Array + defaultFilter?: (row: T) => boolean }): React.ReactElement { - const { columns, rows, keyField, dense, collapseBody, defaultFilter } = props; - let { initialRowsPerPage, rowsPerPageOption } = props; - const [order, setOrder] = React.useState("asc"); - const [orderBy, setOrderBy] = React.useState(0); // index of columns - const [page, setPage] = React.useState(0); - const [filters, setFilters] = React.useState([]); + const { columns, rows, keyField, dense, collapseBody, defaultFilter } = props + let { initialRowsPerPage, rowsPerPageOption } = props + const [order, setOrder] = React.useState("asc") + const [orderBy, setOrderBy] = React.useState(0) // index of columns + const [page, setPage] = React.useState(0) + const [filters, setFilters] = React.useState([]) const getRowIndex = (row: T): number => { - return rows.findIndex((row2) => row[keyField] === row2[keyField]); - }; + return rows.findIndex((row2) => row[keyField] === row2[keyField]) + } // Pagination - rowsPerPageOption = rowsPerPageOption || defaultRowsPerPageOption; + rowsPerPageOption = rowsPerPageOption || defaultRowsPerPageOption initialRowsPerPage = initialRowsPerPage // use first element as default ? initialRowsPerPage : isNumber(rowsPerPageOption[0]) ? rowsPerPageOption[0] - : rowsPerPageOption[0].value; - const [rowsPerPage, setRowsPerPage] = React.useState(initialRowsPerPage); + : rowsPerPageOption[0].value + const [rowsPerPage, setRowsPerPage] = React.useState(initialRowsPerPage) const handleChangePage = (_event: unknown, newPage: number) => { - setPage(newPage); - }; + setPage(newPage) + } const handleChangeRowsPerPage = ( - event: React.ChangeEvent, + event: React.ChangeEvent ) => { - setRowsPerPage(parseInt(event.target.value, 10)); - setPage(0); - }; + setRowsPerPage(parseInt(event.target.value, 10)) + setPage(0) + } // Filtering const fieldAlreadyFiltered = (columnIdx: number): boolean => - filters.some((f) => f.columnIdx === columnIdx); + filters.some((f) => f.columnIdx === columnIdx) const handleClickFilterCell = (columnIdx: number, value: Value) => { if (fieldAlreadyFiltered(columnIdx)) { - return; + return } - const newFilters = [...filters, { columnIdx: columnIdx, value: value }]; - setFilters(newFilters); - }; + const newFilters = [...filters, { columnIdx: columnIdx, value: value }] + setFilters(newFilters) + } const clearFilter = (columnIdx: number): void => { - setFilters(filters.filter((f) => f.columnIdx !== columnIdx)); - }; + setFilters(filters.filter((f) => f.columnIdx !== columnIdx)) + } const filteredRows = rows.filter((row, rowIdx) => { if (defaultFilter?.(row)) { - return false; + return false } return filters.length === 0 ? true : filters.some((f) => { if (columns.length <= f.columnIdx) { console.log( - `columnIdx=${f.columnIdx} must be smaller than columns.length=${columns.length}`, - ); - return true; + `columnIdx=${f.columnIdx} must be smaller than columns.length=${columns.length}` + ) + return true } - const toCellValue = columns[f.columnIdx].toCellValue; + const toCellValue = columns[f.columnIdx].toCellValue if (toCellValue !== undefined) { - return toCellValue(rowIdx) === f.value; + return toCellValue(rowIdx) === f.value } - const field = columns[f.columnIdx].field; - return row[field] === f.value; - }); - }); + const field = columns[f.columnIdx].field + return row[field] === f.value + }) + }) // Sorting const createSortHandler = (columnId: number) => () => { - const isAsc = orderBy === columnId && order === "asc"; - setOrder(isAsc ? "desc" : "asc"); - setOrderBy(columnId); - }; - const sortedRows = stableSort(filteredRows, order, orderBy, columns); + const isAsc = orderBy === columnId && order === "asc" + setOrder(isAsc ? "desc" : "asc") + setOrderBy(columnId) + } + const sortedRows = stableSort(filteredRows, order, orderBy, columns) const currentPageRows = rowsPerPage > 0 ? sortedRows.slice(page * rowsPerPage, page * rowsPerPage + rowsPerPage) - : sortedRows; + : sortedRows const emptyRows = - rowsPerPage - Math.min(rowsPerPage, sortedRows.length - page * rowsPerPage); + rowsPerPage - Math.min(rowsPerPage, sortedRows.length - page * rowsPerPage) const RootDiv = styled("div")({ width: "100%", - }); + }) const HiddenSpan = styled("span")({ border: 0, clip: "rect(0 0 0 0)", @@ -145,10 +145,10 @@ function DataGrid(props: { position: "absolute", top: 20, width: 1, - }); + }) const TableHeaderCellSpan = styled("span")({ display: "inline-flex", - }); + }) return ( @@ -196,7 +196,7 @@ function DataGrid(props: { } color="inherit" onClick={() => { - clearFilter(columnIdx); + clearFilter(columnIdx) }} > @@ -237,16 +237,16 @@ function DataGrid(props: { onRowsPerPageChange={handleChangeRowsPerPage} /> - ); + ) } function DataGridRow(props: { - columns: DataGridColumn[]; - rowIndex: number; - row: T; - keyField: keyof T; - collapseBody?: (rowIndex: number) => React.ReactNode; - handleClickFilterCell: (columnIdx: number, value: Value) => void; + columns: DataGridColumn[] + rowIndex: number + row: T + keyField: keyof T + collapseBody?: (rowIndex: number) => React.ReactNode + handleClickFilterCell: (columnIdx: number, value: Value) => void }) { const { columns, @@ -255,15 +255,15 @@ function DataGridRow(props: { keyField, collapseBody, handleClickFilterCell, - } = props; - const [open, setOpen] = React.useState(false); - const theme = useTheme(); + } = props + const [open, setOpen] = React.useState(false) + const theme = useTheme() const FilterableDiv = styled("div")({ color: theme.palette.primary.main, textDecoration: "underline", cursor: "pointer", - }); + }) return ( @@ -282,7 +282,7 @@ function DataGridRow(props: { const cellItem = column.toCellValue ? column.toCellValue(rowIndex) : // TODO(c-bata): Avoid this implicit type conversion. - (row[column.field] as number | string | null | undefined); + (row[column.field] as number | string | null | undefined) return column.filterable ? ( (props: { const value = column.toCellValue !== undefined ? column.toCellValue(rowIndex) - : row[column.field]; - handleClickFilterCell(columnIndex, value); + : row[column.field] + handleClickFilterCell(columnIndex, value) }} > {cellItem} @@ -305,7 +305,7 @@ function DataGridRow(props: { > {cellItem} - ); + ) })} {collapseBody ? ( @@ -318,66 +318,66 @@ function DataGridRow(props: { ) : null} - ); + ) } function getComparator( order: Order, columns: DataGridColumn[], - orderBy: number, + orderBy: number ): (a: T, b: T) => number { return order === "desc" ? (a, b) => descendingComparator(a, b, columns, orderBy) - : (a, b) => -descendingComparator(a, b, columns, orderBy); + : (a, b) => -descendingComparator(a, b, columns, orderBy) } function descendingComparator( a: T, b: T, columns: DataGridColumn[], - orderBy: number, + orderBy: number ): number { - const field = columns[orderBy].field; + const field = columns[orderBy].field if (b[field] < a[field]) { - return -1; + return -1 } if (b[field] > a[field]) { - return 1; + return 1 } - return 0; + return 0 } function stableSort( array: T[], order: Order, orderBy: number, - columns: DataGridColumn[], + columns: DataGridColumn[] ) { // TODO(c-bata): Refactor here by implementing as the same comparator interface. - const less = columns[orderBy].less; - const comparator = getComparator(order, columns, orderBy); - const stabilizedThis = array.map((el, index) => [el, index] as [T, number]); + const less = columns[orderBy].less + const comparator = getComparator(order, columns, orderBy) + const stabilizedThis = array.map((el, index) => [el, index] as [T, number]) stabilizedThis.sort((a, b) => { if (less) { - const ascending = order === "asc"; + const ascending = order === "asc" const result = ascending ? -less(a[0], b[0], ascending) - : less(a[0], b[0], ascending); - if (result !== 0) return result; + : less(a[0], b[0], ascending) + if (result !== 0) return result } else { - const result = comparator(a[0], b[0]); - if (result !== 0) return result; + const result = comparator(a[0], b[0]) + if (result !== 0) return result } - return a[1] - b[1]; - }); - return stabilizedThis.map((el) => el[0]); + return a[1] - b[1] + }) + return stabilizedThis.map((el) => el[0]) } const isNumber = ( - rowsPerPage: number | { value: number; label: string }, + rowsPerPage: number | { value: number; label: string } ): rowsPerPage is number => { - return typeof rowsPerPage === "number"; -}; + return typeof rowsPerPage === "number" +} -export { DataGrid }; -export type { DataGridColumn }; +export { DataGrid } +export type { DataGridColumn } diff --git a/tslib/react/src/components/PlotHistory.stories.tsx b/tslib/react/src/components/PlotHistory.stories.tsx index 9f390c8b..44d64945 100644 --- a/tslib/react/src/components/PlotHistory.stories.tsx +++ b/tslib/react/src/components/PlotHistory.stories.tsx @@ -1,6 +1,6 @@ -import { Meta, StoryObj } from "@storybook/react"; -import { useMockStudy } from "../MockStudies"; -import { PlotHistory } from "./PlotHistory"; +import { Meta, StoryObj } from "@storybook/react" +import { useMockStudy } from "../MockStudies" +import { PlotHistory } from "./PlotHistory" const meta: Meta = { component: PlotHistory, @@ -8,24 +8,24 @@ const meta: Meta = { tags: ["autodocs"], decorators: [ (Story, storyContext) => { - const study = useMockStudy(storyContext.parameters?.studyId); - if (!study) return

loading...

; + const study = useMockStudy(storyContext.parameters?.studyId) + if (!study) return

loading...

return ( - ); + ) }, ], -}; +} -export default meta; -type Story = StoryObj; +export default meta +type Story = StoryObj export const MockStudy1: Story = { parameters: { studyId: 1, }, -}; +} diff --git a/tslib/react/src/components/PlotHistory.tsx b/tslib/react/src/components/PlotHistory.tsx index c7030cff..1ea68db3 100644 --- a/tslib/react/src/components/PlotHistory.tsx +++ b/tslib/react/src/components/PlotHistory.tsx @@ -12,44 +12,43 @@ import { Switch, Typography, useTheme, -} from "@mui/material"; -import * as Optuna from "@optuna/types"; -import * as plotly from "plotly.js-dist-min"; -import { ChangeEvent, FC, useEffect, useState } from "react"; -import { plotlyDarkTemplate } from "./PlotlyDarkMode"; +} from "@mui/material" +import * as Optuna from "@optuna/types" +import * as plotly from "plotly.js-dist-min" +import { ChangeEvent, FC, useEffect, useState } from "react" +import { plotlyDarkTemplate } from "./PlotlyDarkMode" -const plotDomId = "plot-history"; +const plotDomId = "plot-history" export const PlotHistory: FC<{ - study: Optuna.Study | null; + study: Optuna.Study | null }> = ({ study = null }) => { - const theme = useTheme(); - const [xAxis, setXAxis] = useState("number"); - const [objectiveId, setObjectiveId] = useState(0); - const [logScale, setLogScale] = useState(false); - const [filterCompleteTrial, setFilterCompleteTrial] = - useState(false); - const [filterPrunedTrial, setFilterPrunedTrial] = useState(false); + const theme = useTheme() + const [xAxis, setXAxis] = useState("number") + const [objectiveId, setObjectiveId] = useState(0) + const [logScale, setLogScale] = useState(false) + const [filterCompleteTrial, setFilterCompleteTrial] = useState(false) + const [filterPrunedTrial, setFilterPrunedTrial] = useState(false) const handleObjectiveChange = (event: SelectChangeEvent) => { - setObjectiveId(event.target.value as number); - }; + setObjectiveId(event.target.value as number) + } const handleXAxisChange = (e: ChangeEvent) => { - setXAxis(e.target.value); - }; + setXAxis(e.target.value) + } const handleLogScaleChange = () => { - setLogScale(!logScale); - }; + setLogScale(!logScale) + } const handleFilterCompleteChange = () => { - setFilterCompleteTrial(!filterCompleteTrial); - }; + setFilterCompleteTrial(!filterCompleteTrial) + } const handleFilterPrunedChange = () => { - setFilterPrunedTrial(!filterPrunedTrial); - }; + setFilterPrunedTrial(!filterPrunedTrial) + } useEffect(() => { if (study !== null) { @@ -60,8 +59,8 @@ export const PlotHistory: FC<{ logScale, filterCompleteTrial, filterPrunedTrial, - theme.palette.mode, - ); + theme.palette.mode + ) } }, [ study, @@ -71,7 +70,7 @@ export const PlotHistory: FC<{ filterPrunedTrial, filterCompleteTrial, theme.palette.mode, - ]); + ]) return ( @@ -169,22 +168,22 @@ export const PlotHistory: FC<{
- ); -}; + ) +} const filterFunc = (trial: Optuna.Trial, objectiveId: number): boolean => { if (trial.state !== "Complete" && trial.state !== "Pruned") { - return false; + return false } if (trial.values === undefined) { - return false; + return false } return ( trial.values.length > objectiveId && trial.values[objectiveId] !== Infinity && trial.values[objectiveId] !== -Infinity - ); -}; + ) +} const plotHistory = ( study: Optuna.Study, @@ -193,10 +192,10 @@ const plotHistory = ( logScale: boolean, filterCompleteTrial: boolean, filterPrunedTrial: boolean, - mode: string, + mode: string ) => { if (document.getElementById(plotDomId) === null) { - return; + return } const layout: Partial = { @@ -216,18 +215,18 @@ const plotHistory = ( }, showlegend: true, template: mode === "dark" ? plotlyDarkTemplate : {}, - }; + } - let filteredTrials = study.trials.filter((t) => filterFunc(t, objectiveId)); + let filteredTrials = study.trials.filter((t) => filterFunc(t, objectiveId)) if (filterCompleteTrial) { - filteredTrials = filteredTrials.filter((t) => t.state !== "Complete"); + filteredTrials = filteredTrials.filter((t) => t.state !== "Complete") } if (filterPrunedTrial) { - filteredTrials = filteredTrials.filter((t) => t.state !== "Pruned"); + filteredTrials = filteredTrials.filter((t) => t.state !== "Pruned") } if (filteredTrials.length === 0) { - plotly.react(plotDomId, [], layout); - return; + plotly.react(plotDomId, [], layout) + return } const getAxisX = (trial: Optuna.Trial): number | Date => { @@ -235,71 +234,71 @@ const plotHistory = ( ? trial.number : xAxis === "datetime_start" ? trial.datetime_start ?? new Date() - : trial.datetime_complete ?? new Date(); - }; + : trial.datetime_complete ?? new Date() + } const getValue = ( trial: Optuna.Trial, - objectiveId: number, + objectiveId: number ): number | null => { if ( objectiveId === null || trial.values === undefined || trial.values.length <= objectiveId ) { - return null; + return null } - const value = trial.values[objectiveId]; + const value = trial.values[objectiveId] if (value === Infinity || value === -Infinity) { - return null; + return null } - return value; - }; + return value + } - const xForLinePlot: (number | Date)[] = []; - const yForLinePlot: number[] = []; - let currentBest: number | null = null; + const xForLinePlot: (number | Date)[] = [] + const yForLinePlot: number[] = [] + let currentBest: number | null = null for (let i = 0; i < filteredTrials.length; i++) { - const t = filteredTrials[i]; - const v = getValue(t, objectiveId) as number; + const t = filteredTrials[i] + const v = getValue(t, objectiveId) as number if (currentBest === null) { - currentBest = v; - xForLinePlot.push(getAxisX(t)); - yForLinePlot.push(v); + currentBest = v + xForLinePlot.push(getAxisX(t)) + yForLinePlot.push(v) } else if ( study.directions[objectiveId] === "maximize" && v > currentBest ) { - const p = filteredTrials[i - 1]; + const p = filteredTrials[i - 1] if (!xForLinePlot.includes(getAxisX(p))) { - xForLinePlot.push(getAxisX(p)); - yForLinePlot.push(currentBest); + xForLinePlot.push(getAxisX(p)) + yForLinePlot.push(currentBest) } - currentBest = v; - xForLinePlot.push(getAxisX(t)); - yForLinePlot.push(v); + currentBest = v + xForLinePlot.push(getAxisX(t)) + yForLinePlot.push(v) } else if ( study.directions[objectiveId] === "minimize" && v < currentBest ) { - const p = filteredTrials[i - 1]; + const p = filteredTrials[i - 1] if (!xForLinePlot.includes(getAxisX(p))) { - xForLinePlot.push(getAxisX(p)); - yForLinePlot.push(currentBest); + xForLinePlot.push(getAxisX(p)) + yForLinePlot.push(currentBest) } - currentBest = v; - xForLinePlot.push(getAxisX(t)); - yForLinePlot.push(v); + currentBest = v + xForLinePlot.push(getAxisX(t)) + yForLinePlot.push(v) } } - xForLinePlot.push(getAxisX(filteredTrials[filteredTrials.length - 1])); - yForLinePlot.push(yForLinePlot[yForLinePlot.length - 1]); + xForLinePlot.push(getAxisX(filteredTrials[filteredTrials.length - 1])) + yForLinePlot.push(yForLinePlot[yForLinePlot.length - 1]) const plotData: Partial[] = [ { x: filteredTrials.map(getAxisX), y: filteredTrials.map( - (t: Optuna.Trial): number => getValue(t, objectiveId) as number, + (t: Optuna.Trial): number => getValue(t, objectiveId) as number ), name: "Objective Value", mode: "markers", @@ -312,6 +311,6 @@ const plotHistory = ( mode: "lines", type: "scatter", }, - ]; - plotly.react(plotDomId, plotData, layout); -}; + ] + plotly.react(plotDomId, plotData, layout) +} diff --git a/tslib/react/src/components/PlotHistoryDark.stories.tsx b/tslib/react/src/components/PlotHistoryDark.stories.tsx index c4d6f07c..a25c217e 100644 --- a/tslib/react/src/components/PlotHistoryDark.stories.tsx +++ b/tslib/react/src/components/PlotHistoryDark.stories.tsx @@ -1,8 +1,8 @@ -import { ThemeProvider } from "@mui/material"; -import { Meta, StoryObj } from "@storybook/react"; -import { useMockStudy } from "../MockStudies"; -import { darkTheme } from "../styles/darkTheme"; -import { PlotHistory } from "./PlotHistory"; +import { ThemeProvider } from "@mui/material" +import { Meta, StoryObj } from "@storybook/react" +import { useMockStudy } from "../MockStudies" +import { darkTheme } from "../styles/darkTheme" +import { PlotHistory } from "./PlotHistory" const meta: Meta = { component: PlotHistory, @@ -10,8 +10,8 @@ const meta: Meta = { tags: ["autodocs"], decorators: [ (Story, storyContext) => { - const study = useMockStudy(storyContext.parameters?.studyId); - if (!study) return

loading...

; + const study = useMockStudy(storyContext.parameters?.studyId) + if (!study) return

loading...

return ( = { }} /> - ); + ) }, ], parameters: { backgrounds: { default: "dark" }, }, -}; +} -export default meta; -type Story = StoryObj; +export default meta +type Story = StoryObj export const MockStudy1: Story = { parameters: { studyId: 1, }, -}; +} diff --git a/tslib/react/src/components/TrialTable.stories.tsx b/tslib/react/src/components/TrialTable.stories.tsx index 5d6e862d..6ea1ee73 100644 --- a/tslib/react/src/components/TrialTable.stories.tsx +++ b/tslib/react/src/components/TrialTable.stories.tsx @@ -1,6 +1,6 @@ -import { Meta, StoryObj } from "@storybook/react"; -import { useMockStudy } from "../MockStudies"; -import { TrialTable } from "./TrialTable"; +import { Meta, StoryObj } from "@storybook/react" +import { useMockStudy } from "../MockStudies" +import { TrialTable } from "./TrialTable" const meta: Meta = { component: TrialTable, @@ -8,24 +8,24 @@ const meta: Meta = { tags: ["autodocs"], decorators: [ (Story, storyContext) => { - const study = useMockStudy(storyContext.parameters?.studyId); - if (!study) return

loading...

; + const study = useMockStudy(storyContext.parameters?.studyId) + if (!study) return

loading...

return ( - ); + ) }, ], -}; +} -export default meta; -type Story = StoryObj; +export default meta +type Story = StoryObj export const MockStudy1: Story = { parameters: { studyId: 1, }, -}; +} diff --git a/tslib/react/src/components/TrialTable.tsx b/tslib/react/src/components/TrialTable.tsx index c9cca652..46826e82 100644 --- a/tslib/react/src/components/TrialTable.tsx +++ b/tslib/react/src/components/TrialTable.tsx @@ -1,13 +1,13 @@ -import { FC } from "react"; +import { FC } from "react" -import * as Optuna from "@optuna/types"; -import { DataGrid, DataGridColumn } from "./DataGrid"; +import * as Optuna from "@optuna/types" +import { DataGrid, DataGridColumn } from "./DataGrid" export const TrialTable: FC<{ - study: Optuna.Study; - initialRowsPerPage?: number; + study: Optuna.Study + initialRowsPerPage?: number }> = ({ study, initialRowsPerPage }) => { - const trials: Optuna.Trial[] = study.trials; + const trials: Optuna.Trial[] = study.trials const columns: DataGridColumn[] = [ { field: "number", label: "Number", sortable: true, padding: "none" }, @@ -19,7 +19,7 @@ export const TrialTable: FC<{ padding: "none", toCellValue: (i) => trials[i].state.toString(), }, - ]; + ] if (study === null || study.directions.length === 1) { columns.push({ @@ -27,27 +27,27 @@ export const TrialTable: FC<{ label: "Value", sortable: true, less: (firstEl, secondEl, ascending): number => { - const firstVal = firstEl.values?.[0]; - const secondVal = secondEl.values?.[0]; + const firstVal = firstEl.values?.[0] + const secondVal = secondEl.values?.[0] if (firstVal === secondVal) { - return 0; + return 0 } if (firstVal === undefined) { - return ascending ? -1 : 1; + return ascending ? -1 : 1 } if (secondVal === undefined) { - return ascending ? 1 : -1; + return ascending ? 1 : -1 } - return firstVal < secondVal ? 1 : -1; + return firstVal < secondVal ? 1 : -1 }, toCellValue: (i) => { if (trials[i].values === undefined) { - return null; + return null } - return trials[i].values?.[0]; + return trials[i].values?.[0] }, - }); + }) } else { const objectiveColumns: DataGridColumn[] = study.directions.map((_s, objectiveId) => ({ @@ -55,31 +55,31 @@ export const TrialTable: FC<{ label: `Objective ${objectiveId}`, sortable: true, less: (firstEl, secondEl, ascending): number => { - const firstVal = firstEl.values?.[objectiveId]; - const secondVal = secondEl.values?.[objectiveId]; + const firstVal = firstEl.values?.[objectiveId] + const secondVal = secondEl.values?.[objectiveId] if (firstVal === secondVal) { - return 0; + return 0 } if (firstVal === undefined) { - return ascending ? -1 : 1; + return ascending ? -1 : 1 } if (secondVal === undefined) { - return ascending ? 1 : -1; + return ascending ? 1 : -1 } - return firstVal < secondVal ? 1 : -1; + return firstVal < secondVal ? 1 : -1 }, toCellValue: (i) => { if (trials[i].values === undefined) { - return null; + return null } - return trials[i].values?.[objectiveId]; + return trials[i].values?.[objectiveId] }, - })); - columns.push(...objectiveColumns); + })) + columns.push(...objectiveColumns) } - study.union_search_space.forEach((s) => { + for (const s of study.union_search_space) { columns.push({ field: "params", label: `Param ${s.name}`, @@ -90,27 +90,27 @@ export const TrialTable: FC<{ filterable: false, less: (firstEl, secondEl): number => { const firstVal = firstEl.params.find( - (p) => p.name === s.name, - )?.param_internal_value; + (p) => p.name === s.name + )?.param_internal_value const secondVal = secondEl.params.find( - (p) => p.name === s.name, - )?.param_internal_value; + (p) => p.name === s.name + )?.param_internal_value if (firstVal === secondVal) { - return 0; + return 0 } if (firstVal && secondVal) { - return firstVal < secondVal ? 1 : -1; + return firstVal < secondVal ? 1 : -1 } if (firstVal) { - return -1; + return -1 } - return 1; + return 1 }, - }); - }); + }) + } - study.union_user_attrs.forEach((attr_spec) => { + for (const attr_spec of study.union_user_attrs) { columns.push({ field: "user_attrs", label: `UserAttribute ${attr_spec.key}`, @@ -121,25 +121,25 @@ export const TrialTable: FC<{ filterable: false, less: (firstEl, secondEl): number => { const firstVal = firstEl.user_attrs.find( - (attr) => attr.key === attr_spec.key, - )?.value; + (attr) => attr.key === attr_spec.key + )?.value const secondVal = secondEl.user_attrs.find( - (attr) => attr.key === attr_spec.key, - )?.value; + (attr) => attr.key === attr_spec.key + )?.value if (firstVal === secondVal) { - return 0; + return 0 } if (firstVal && secondVal) { - return firstVal < secondVal ? 1 : -1; + return firstVal < secondVal ? 1 : -1 } if (firstVal) { - return -1; + return -1 } - return 1; + return 1 }, - }); - }); + }) + } return ( @@ -149,5 +149,5 @@ export const TrialTable: FC<{ dense={false} initialRowsPerPage={initialRowsPerPage} /> - ); -}; + ) +} diff --git a/tslib/react/src/components/TrialTableDark.stories.tsx b/tslib/react/src/components/TrialTableDark.stories.tsx index ea7dbe74..8c671da4 100644 --- a/tslib/react/src/components/TrialTableDark.stories.tsx +++ b/tslib/react/src/components/TrialTableDark.stories.tsx @@ -1,8 +1,8 @@ -import { ThemeProvider } from "@mui/material"; -import { Meta, StoryObj } from "@storybook/react"; -import { useMockStudy } from "../MockStudies"; -import { darkTheme } from "../styles/darkTheme"; -import { TrialTable } from "./TrialTable"; +import { ThemeProvider } from "@mui/material" +import { Meta, StoryObj } from "@storybook/react" +import { useMockStudy } from "../MockStudies" +import { darkTheme } from "../styles/darkTheme" +import { TrialTable } from "./TrialTable" const meta: Meta = { component: TrialTable, @@ -10,8 +10,8 @@ const meta: Meta = { tags: ["autodocs"], decorators: [ (Story, storyContext) => { - const study = useMockStudy(storyContext.parameters?.studyId); - if (!study) return

loading...

; + const study = useMockStudy(storyContext.parameters?.studyId) + if (!study) return

loading...

return ( = { }} /> - ); + ) }, ], parameters: { backgrounds: { default: "dark" }, }, -}; +} -export default meta; -type Story = StoryObj; +export default meta +type Story = StoryObj export const MockStudy1: Story = { parameters: { studyId: 1, }, -}; +} diff --git a/tslib/react/src/index.ts b/tslib/react/src/index.ts index 48e4ecb2..a45638ab 100644 --- a/tslib/react/src/index.ts +++ b/tslib/react/src/index.ts @@ -1,5 +1,5 @@ -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 { 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" diff --git a/tslib/react/src/styles/darkTheme.ts b/tslib/react/src/styles/darkTheme.ts index 28a97406..b09d2a3e 100644 --- a/tslib/react/src/styles/darkTheme.ts +++ b/tslib/react/src/styles/darkTheme.ts @@ -1,7 +1,7 @@ -import { createTheme } from "@mui/material"; +import { createTheme } from "@mui/material" export const darkTheme = createTheme({ palette: { mode: "dark", }, -}); +}) diff --git a/tslib/react/src/utils/loadStorageFromFile.ts b/tslib/react/src/utils/loadStorageFromFile.ts index f9097e49..eac68ac4 100644 --- a/tslib/react/src/utils/loadStorageFromFile.ts +++ b/tslib/react/src/utils/loadStorageFromFile.ts @@ -1,45 +1,45 @@ -import { JournalFileStorage, SQLite3Storage } from "@optuna/storage"; -import * as Optuna from "@optuna/types"; -import { SetterOrUpdater } from "recoil"; +import { JournalFileStorage, SQLite3Storage } from "@optuna/storage" +import * as Optuna from "@optuna/types" +import { SetterOrUpdater } from "recoil" const readFile = async (file: File) => { return new Promise((resolve, reject) => { - const reader = new FileReader(); + const reader = new FileReader() reader.addEventListener("load", () => { - const arrayBuffer = reader.result as ArrayBuffer | null; + const arrayBuffer = reader.result as ArrayBuffer | null if (arrayBuffer !== null) { - resolve(arrayBuffer); + resolve(arrayBuffer) } else { - reject(new Error("Failed to load file")); + reject(new Error("Failed to load file")) } - }); - reader.readAsArrayBuffer(file); - }); -}; + }) + reader.readAsArrayBuffer(file) + }) +} const loadStudiesFromStorage = async ( storage: SQLite3Storage | JournalFileStorage, - setter: SetterOrUpdater, + setter: SetterOrUpdater ) => { - const studySummaries = await storage.getStudies(); + const studySummaries = await storage.getStudies() const studies = ( await Promise.all( - studySummaries.map((_summary, index) => storage.getStudy(index)), + studySummaries.map((_summary, index) => storage.getStudy(index)) ) - ).filter((s) => s !== null) as Optuna.Study[]; - setter((prev) => [...prev, ...studies]); -}; + ).filter((s) => s !== null) as Optuna.Study[] + setter((prev) => [...prev, ...studies]) +} export const loadStorageFromFile = async ( file: File, - setStudies: SetterOrUpdater, + setStudies: SetterOrUpdater ) => { - const arrayBuf = await readFile(file); - const header = new Uint8Array(arrayBuf, 0, 16); - const headerString = new TextDecoder().decode(header); + const arrayBuf = await readFile(file) + const header = new Uint8Array(arrayBuf, 0, 16) + const headerString = new TextDecoder().decode(header) if (headerString === "SQLite format 3\u0000") { - await loadStudiesFromStorage(new SQLite3Storage(arrayBuf), setStudies); + await loadStudiesFromStorage(new SQLite3Storage(arrayBuf), setStudies) } else { - await loadStudiesFromStorage(new JournalFileStorage(arrayBuf), setStudies); + await loadStudiesFromStorage(new JournalFileStorage(arrayBuf), setStudies) } -}; +} diff --git a/tslib/react/vite.config.ts b/tslib/react/vite.config.ts index 07025b63..afe91010 100644 --- a/tslib/react/vite.config.ts +++ b/tslib/react/vite.config.ts @@ -1,5 +1,5 @@ -import react from "@vitejs/plugin-react-swc"; -import { defineConfig } from "vite"; +import react from "@vitejs/plugin-react-swc" +import { defineConfig } from "vite" // https://vitejs.dev/config/ export default defineConfig({ @@ -13,4 +13,4 @@ export default defineConfig({ "Cross-Origin-Embedder-Policy": "require-corp", }, }, -}); +})