From f04c2d7d7bae0cd667eec056bc0a35b19f30b846 Mon Sep 17 00:00:00 2001 From: keisuke-umezawa Date: Sun, 5 Nov 2023 16:32:18 +0900 Subject: [PATCH] Move getAxis implementation to graphUtil --- .../ts/components/GraphContour.tsx | 109 +---------------- optuna_dashboard/ts/graphUtil.ts | 112 ++++++++++++++++++ 2 files changed, 113 insertions(+), 108 deletions(-) diff --git a/optuna_dashboard/ts/components/GraphContour.tsx b/optuna_dashboard/ts/components/GraphContour.tsx index a5416f5b..b7895ddf 100644 --- a/optuna_dashboard/ts/components/GraphContour.tsx +++ b/optuna_dashboard/ts/components/GraphContour.tsx @@ -14,25 +14,8 @@ import { import blue from "@mui/material/colors/blue" import { plotlyDarkTemplate } from "./PlotlyDarkMode" import { useMergedUnionSearchSpace } from "../searchSpace" +import { getAxisInfo } from "../graphUtil" -// eslint-disable-next-line @typescript-eslint/no-explicit-any -const unique = (array: any[]) => { - const knownElements = new Map() - array.forEach((elem) => knownElements.set(elem, true)) - return Array.from(knownElements.keys()) -} - -type AxisInfo = { - name: string - min: number - max: number - isLog: boolean - isCat: boolean - indices: (string | number)[] - values: (string | number | null)[] -} - -const PADDING_RATIO = 0.05 const plotDomId = "graph-contour" export const Contour: FC<{ @@ -284,93 +267,3 @@ const plotContour = ( ] plotly.react(plotDomId, plotData, layout) } - -const getAxisInfoForNumericalParams = ( - trials: Trial[], - paramName: string, - distribution: FloatDistribution | IntDistribution -): AxisInfo => { - let min = 0 - let max = 0 - if (distribution.log) { - const padding = - (Math.log10(distribution.high) - Math.log10(distribution.low)) * - PADDING_RATIO - min = Math.pow(10, Math.log10(distribution.low) - padding) - max = Math.pow(10, Math.log10(distribution.high) + padding) - } else { - const padding = (distribution.high - distribution.low) * PADDING_RATIO - min = distribution.low - padding - max = distribution.high + padding - } - - const values = trials.map( - (trial) => - trial.params.find((p) => p.name === paramName)?.param_internal_value || - null - ) - const indices = unique(values) - .filter((v) => v !== null) - .sort((a, b) => a - b) - if (indices.length >= 2) { - indices.unshift(min) - indices.push(max) - } - return { - name: paramName, - min, - max, - isLog: distribution.log, - isCat: false, - indices, - values, - } -} - -const getAxisInfoForCategoricalParams = ( - trials: Trial[], - paramName: string, - distribution: CategoricalDistribution -): AxisInfo => { - const values = trials.map( - (trial) => - trial.params.find((p) => p.name === paramName)?.param_external_value || - null - ) - const isDynamic = values.some((v) => v === null) - const span = distribution.choices.length - (isDynamic ? 2 : 1) - const padding = span * PADDING_RATIO - const min = -padding - const max = span + padding - - const indices = distribution.choices - .map((c) => c.value) - .sort((a, b) => - a.toLowerCase() < b.toLowerCase() - ? -1 - : a.toLowerCase() > b.toLowerCase() - ? 1 - : 0 - ) - return { - name: paramName, - min, - max, - isLog: false, - isCat: true, - indices, - values, - } -} - -const getAxisInfo = (trials: Trial[], param: SearchSpaceItem): AxisInfo => { - if (param.distribution.type === "CategoricalDistribution") { - return getAxisInfoForCategoricalParams( - trials, - param.name, - param.distribution - ) - } else { - return getAxisInfoForNumericalParams(trials, param.name, param.distribution) - } -} diff --git a/optuna_dashboard/ts/graphUtil.ts b/optuna_dashboard/ts/graphUtil.ts index 1b1c69cb..4e6f74e8 100644 --- a/optuna_dashboard/ts/graphUtil.ts +++ b/optuna_dashboard/ts/graphUtil.ts @@ -1,3 +1,115 @@ +const PADDING_RATIO = 0.05 + +type AxisInfo = { + name: string + min: number + max: number + isLog: boolean + isCat: boolean + indices: (string | number)[] + values: (string | number | null)[] +} + +// eslint-disable-next-line @typescript-eslint/no-explicit-any +const unique = (array: any[]) => { + const knownElements = new Map() + array.forEach((elem) => knownElements.set(elem, true)) + return Array.from(knownElements.keys()) +} + +export const getAxisInfo = ( + trials: Trial[], + param: SearchSpaceItem +): AxisInfo => { + if (param.distribution.type === "CategoricalDistribution") { + return getAxisInfoForCategoricalParams( + trials, + param.name, + param.distribution + ) + } else { + return getAxisInfoForNumericalParams(trials, param.name, param.distribution) + } +} + +const getAxisInfoForCategoricalParams = ( + trials: Trial[], + paramName: string, + distribution: CategoricalDistribution +): AxisInfo => { + const values = trials.map( + (trial) => + trial.params.find((p) => p.name === paramName)?.param_external_value || + null + ) + const isDynamic = values.some((v) => v === null) + const span = distribution.choices.length - (isDynamic ? 2 : 1) + const padding = span * PADDING_RATIO + const min = -padding + const max = span + padding + + const indices = distribution.choices + .map((c) => c.value) + .sort((a, b) => + a.toLowerCase() < b.toLowerCase() + ? -1 + : a.toLowerCase() > b.toLowerCase() + ? 1 + : 0 + ) + return { + name: paramName, + min, + max, + isLog: false, + isCat: true, + indices, + values, + } +} + +const getAxisInfoForNumericalParams = ( + trials: Trial[], + paramName: string, + distribution: FloatDistribution | IntDistribution +): AxisInfo => { + let min = 0 + let max = 0 + if (distribution.log) { + const padding = + (Math.log10(distribution.high) - Math.log10(distribution.low)) * + PADDING_RATIO + min = Math.pow(10, Math.log10(distribution.low) - padding) + max = Math.pow(10, Math.log10(distribution.high) + padding) + } else { + const padding = (distribution.high - distribution.low) * PADDING_RATIO + min = distribution.low - padding + max = distribution.high + padding + } + + const values = trials.map( + (trial) => + trial.params.find((p) => p.name === paramName)?.param_internal_value || + null + ) + const indices = unique(values) + .filter((v) => v !== null) + .sort((a, b) => a - b) + if (indices.length >= 2) { + indices.unshift(min) + indices.push(max) + } + return { + name: paramName, + min, + max, + isLog: distribution.log, + isCat: false, + indices, + values, + } +} + export const makeHovertext = (trial: Trial): string => { return JSON.stringify( {