Delete biome dependencies from tslib

This commit is contained in:
porink0424
2024-03-29 18:04:50 +09:00
parent 605fde2ca6
commit bd4554e594
20 changed files with 344 additions and 416 deletions
+1
View File
@@ -14,6 +14,7 @@
],
"ignore": [
"optuna_dashboard/ts/components/PlotlyColorTemplates.ts",
"tslib/react/src/components/PlotlyDarkMode.ts",
"standalone_app/src/PlotlyDarkMode.ts",
"tslib/**/pkg/*"
]
-42
View File
@@ -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,
@@ -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
+5 -5
View File
@@ -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
+3 -3
View File
@@ -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
-26
View File
@@ -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"
}
}
-1
View File
@@ -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",
-3
View File
@@ -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",
+21 -21
View File
@@ -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<Optuna.Study[]>([]);
const [mockStudies, setMockStudies] = useState<Optuna.Study[]>([])
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)
}
+111 -111
View File
@@ -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: <explanation>
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<T> {
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<T>(props: {
columns: DataGridColumn<T>[];
rows: T[];
keyField: keyof T;
dense?: boolean;
collapseBody?: (rowIndex: number) => React.ReactNode;
initialRowsPerPage?: number;
rowsPerPageOption?: Array<number | { value: number; label: string }>;
defaultFilter?: (row: T) => boolean;
columns: DataGridColumn<T>[]
rows: T[]
keyField: keyof T
dense?: boolean
collapseBody?: (rowIndex: number) => React.ReactNode
initialRowsPerPage?: number
rowsPerPageOption?: Array<number | { value: number; label: string }>
defaultFilter?: (row: T) => boolean
}): React.ReactElement {
const { columns, rows, keyField, dense, collapseBody, defaultFilter } = props;
let { initialRowsPerPage, rowsPerPageOption } = props;
const [order, setOrder] = React.useState<Order>("asc");
const [orderBy, setOrderBy] = React.useState<number>(0); // index of columns
const [page, setPage] = React.useState(0);
const [filters, setFilters] = React.useState<RowFilter[]>([]);
const { columns, rows, keyField, dense, collapseBody, defaultFilter } = props
let { initialRowsPerPage, rowsPerPageOption } = props
const [order, setOrder] = React.useState<Order>("asc")
const [orderBy, setOrderBy] = React.useState<number>(0) // index of columns
const [page, setPage] = React.useState(0)
const [filters, setFilters] = React.useState<RowFilter[]>([])
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<HTMLInputElement>,
event: React.ChangeEvent<HTMLInputElement>
) => {
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<T>(filteredRows, order, orderBy, columns);
const isAsc = orderBy === columnId && order === "asc"
setOrder(isAsc ? "desc" : "asc")
setOrderBy(columnId)
}
const sortedRows = stableSort<T>(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<T>(props: {
position: "absolute",
top: 20,
width: 1,
});
})
const TableHeaderCellSpan = styled("span")({
display: "inline-flex",
});
})
return (
<RootDiv>
<TableContainer>
@@ -196,7 +196,7 @@ function DataGrid<T>(props: {
}
color="inherit"
onClick={() => {
clearFilter(columnIdx);
clearFilter(columnIdx)
}}
>
<Clear />
@@ -237,16 +237,16 @@ function DataGrid<T>(props: {
onRowsPerPageChange={handleChangeRowsPerPage}
/>
</RootDiv>
);
)
}
function DataGridRow<T>(props: {
columns: DataGridColumn<T>[];
rowIndex: number;
row: T;
keyField: keyof T;
collapseBody?: (rowIndex: number) => React.ReactNode;
handleClickFilterCell: (columnIdx: number, value: Value) => void;
columns: DataGridColumn<T>[]
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<T>(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 (
<React.Fragment>
<TableRow hover tabIndex={-1}>
@@ -282,7 +282,7 @@ function DataGridRow<T>(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 ? (
<TableCell
@@ -292,8 +292,8 @@ function DataGridRow<T>(props: {
const value =
column.toCellValue !== undefined
? column.toCellValue(rowIndex)
: row[column.field];
handleClickFilterCell(columnIndex, value);
: row[column.field]
handleClickFilterCell(columnIndex, value)
}}
>
<FilterableDiv>{cellItem}</FilterableDiv>
@@ -305,7 +305,7 @@ function DataGridRow<T>(props: {
>
{cellItem}
</TableCell>
);
)
})}
</TableRow>
{collapseBody ? (
@@ -318,66 +318,66 @@ function DataGridRow<T>(props: {
</TableRow>
) : null}
</React.Fragment>
);
)
}
function getComparator<T>(
order: Order,
columns: DataGridColumn<T>[],
orderBy: number,
orderBy: number
): (a: T, b: T) => number {
return order === "desc"
? (a, b) => descendingComparator<T>(a, b, columns, orderBy)
: (a, b) => -descendingComparator<T>(a, b, columns, orderBy);
: (a, b) => -descendingComparator<T>(a, b, columns, orderBy)
}
function descendingComparator<T>(
a: T,
b: T,
columns: DataGridColumn<T>[],
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<T>(
array: T[],
order: Order,
orderBy: number,
columns: DataGridColumn<T>[],
columns: DataGridColumn<T>[]
) {
// 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 }
@@ -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<typeof PlotHistory> = {
component: PlotHistory,
@@ -8,24 +8,24 @@ const meta: Meta<typeof PlotHistory> = {
tags: ["autodocs"],
decorators: [
(Story, storyContext) => {
const study = useMockStudy(storyContext.parameters?.studyId);
if (!study) return <p>loading...</p>;
const study = useMockStudy(storyContext.parameters?.studyId)
if (!study) return <p>loading...</p>
return (
<Story
args={{
study,
}}
/>
);
)
},
],
};
}
export default meta;
type Story = StoryObj<typeof PlotHistory>;
export default meta
type Story = StoryObj<typeof PlotHistory>
export const MockStudy1: Story = {
parameters: {
studyId: 1,
},
};
}
+74 -75
View File
@@ -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<string>("number");
const [objectiveId, setObjectiveId] = useState<number>(0);
const [logScale, setLogScale] = useState<boolean>(false);
const [filterCompleteTrial, setFilterCompleteTrial] =
useState<boolean>(false);
const [filterPrunedTrial, setFilterPrunedTrial] = useState<boolean>(false);
const theme = useTheme()
const [xAxis, setXAxis] = useState<string>("number")
const [objectiveId, setObjectiveId] = useState<number>(0)
const [logScale, setLogScale] = useState<boolean>(false)
const [filterCompleteTrial, setFilterCompleteTrial] = useState<boolean>(false)
const [filterPrunedTrial, setFilterPrunedTrial] = useState<boolean>(false)
const handleObjectiveChange = (event: SelectChangeEvent<number>) => {
setObjectiveId(event.target.value as number);
};
setObjectiveId(event.target.value as number)
}
const handleXAxisChange = (e: ChangeEvent<HTMLInputElement>) => {
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 (
<Grid container direction="row">
@@ -169,22 +168,22 @@ export const PlotHistory: FC<{
<div id={plotDomId} />
</Grid>
</Grid>
);
};
)
}
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<plotly.Layout> = {
@@ -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<plotly.PlotData>[] = [
{
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)
}
@@ -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<typeof PlotHistory> = {
component: PlotHistory,
@@ -10,8 +10,8 @@ const meta: Meta<typeof PlotHistory> = {
tags: ["autodocs"],
decorators: [
(Story, storyContext) => {
const study = useMockStudy(storyContext.parameters?.studyId);
if (!study) return <p>loading...</p>;
const study = useMockStudy(storyContext.parameters?.studyId)
if (!study) return <p>loading...</p>
return (
<ThemeProvider theme={darkTheme}>
<Story
@@ -20,19 +20,19 @@ const meta: Meta<typeof PlotHistory> = {
}}
/>
</ThemeProvider>
);
)
},
],
parameters: {
backgrounds: { default: "dark" },
},
};
}
export default meta;
type Story = StoryObj<typeof PlotHistory>;
export default meta
type Story = StoryObj<typeof PlotHistory>
export const MockStudy1: Story = {
parameters: {
studyId: 1,
},
};
}
@@ -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<typeof TrialTable> = {
component: TrialTable,
@@ -8,24 +8,24 @@ const meta: Meta<typeof TrialTable> = {
tags: ["autodocs"],
decorators: [
(Story, storyContext) => {
const study = useMockStudy(storyContext.parameters?.studyId);
if (!study) return <p>loading...</p>;
const study = useMockStudy(storyContext.parameters?.studyId)
if (!study) return <p>loading...</p>
return (
<Story
args={{
study,
}}
/>
);
)
},
],
};
}
export default meta;
type Story = StoryObj<typeof TrialTable>;
export default meta
type Story = StoryObj<typeof TrialTable>
export const MockStudy1: Story = {
parameters: {
studyId: 1,
},
};
}
+50 -50
View File
@@ -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<Optuna.Trial>[] = [
{ 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<Optuna.Trial>[] =
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 (
<DataGrid<Optuna.Trial>
@@ -149,5 +149,5 @@ export const TrialTable: FC<{
dense={false}
initialRowsPerPage={initialRowsPerPage}
/>
);
};
)
}
@@ -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<typeof TrialTable> = {
component: TrialTable,
@@ -10,8 +10,8 @@ const meta: Meta<typeof TrialTable> = {
tags: ["autodocs"],
decorators: [
(Story, storyContext) => {
const study = useMockStudy(storyContext.parameters?.studyId);
if (!study) return <p>loading...</p>;
const study = useMockStudy(storyContext.parameters?.studyId)
if (!study) return <p>loading...</p>
return (
<ThemeProvider theme={darkTheme}>
<Story
@@ -20,19 +20,19 @@ const meta: Meta<typeof TrialTable> = {
}}
/>
</ThemeProvider>
);
)
},
],
parameters: {
backgrounds: { default: "dark" },
},
};
}
export default meta;
type Story = StoryObj<typeof TrialTable>;
export default meta
type Story = StoryObj<typeof TrialTable>
export const MockStudy1: Story = {
parameters: {
studyId: 1,
},
};
}
+5 -5
View File
@@ -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"
+2 -2
View File
@@ -1,7 +1,7 @@
import { createTheme } from "@mui/material";
import { createTheme } from "@mui/material"
export const darkTheme = createTheme({
palette: {
mode: "dark",
},
});
})
+24 -24
View File
@@ -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<ArrayBuffer>((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<Optuna.Study[]>,
setter: SetterOrUpdater<Optuna.Study[]>
) => {
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<Optuna.Study[]>,
setStudies: SetterOrUpdater<Optuna.Study[]>
) => {
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)
}
};
}
+3 -3
View File
@@ -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",
},
},
});
})