import * as plotly from "plotly.js-dist" import React, { ChangeEvent, FC, useEffect, useState } from "react" import { Grid, FormControl, FormLabel, InputLabel, MenuItem, Switch, Select, Typography, SelectChangeEvent, useTheme, Box, } from "@mui/material" import { plotlyDarkTemplate } from "./PlotlyDarkMode" const plotDomId = "graph-slice" // TODO(c-bata): Check `log` field of IntDistribution and FloatDistribution. const logDistributions = ["LogUniformDistribution", "IntLogUniformDistribution"] export const GraphSlice: FC<{ study: StudyDetail | null }> = ({ study = null }) => { const theme = useTheme() const trials: Trial[] = study !== null ? study.trials : [] const [objectiveId, setObjectiveId] = useState(0) const [selected, setSelected] = useState(null) const [logXScale, setLogXScale] = useState(false) const [logYScale, setLogYScale] = useState(false) const paramNames = study?.union_search_space.map((s) => s.name) const distributions = new Map( study?.union_search_space.map((s) => [s.name, s.distribution]) ) if (selected === null && paramNames && paramNames.length > 0) { const distribution = distributions.get(paramNames[0]) || "" setSelected(paramNames[0]) setLogXScale(logDistributions.includes(distribution)) } useEffect(() => { plotSlice( trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode ) }, [trials, objectiveId, selected, logXScale, logYScale, theme.palette.mode]) const handleObjectiveChange = (event: SelectChangeEvent) => { setObjectiveId(event.target.value as number) } const handleSelectedParam = (e: SelectChangeEvent) => { const paramName = e.target.value const distribution = distributions.get(paramName) || "" setSelected(paramName) setLogXScale(logDistributions.includes(distribution)) } const handleLogYScaleChange = (e: ChangeEvent) => { e.preventDefault() setLogYScale(!logYScale) } return ( Slice {study !== null && study.directions.length !== 1 ? ( Objective ID: ) : null} Parameter Log y scale: ) } const filterFunc = ( trial: Trial, objectiveId: number, selected: string | null ): boolean => { if (trial.state !== "Complete" && trial.state !== "Pruned") { return false } if (trial.params.find((p) => p.name == selected) === undefined) { return false } if (trial.values === undefined) { return false } return ( trial.values.length > objectiveId && trial.values[objectiveId] !== "inf" ) } const plotSlice = ( trials: Trial[], objectiveId: number, selected: string | null, logXScale: boolean, logYScale: boolean, mode: string ) => { if (document.getElementById(plotDomId) === null) { return } const layout: Partial = { margin: { l: 50, t: 0, r: 50, b: 0, }, xaxis: { title: selected || "", type: logXScale ? "log" : "linear", gridwidth: 1, automargin: true, }, yaxis: { title: "Objective Values", type: logYScale ? "log" : "linear", gridwidth: 1, automargin: true, }, showlegend: false, template: mode === "dark" ? plotlyDarkTemplate : {}, } const filteredTrials = trials.filter((t) => filterFunc(t, objectiveId, selected) ) if (filteredTrials.length === 0 || selected === null) { plotly.react(plotDomId, [], layout) return } const objectiveValues: number[] = filteredTrials.map( (t) => t.values![objectiveId] as number ) const valueStrings = filteredTrials.map((t) => { return t.params.find((p) => p.name == selected)!.value }) const isnum = valueStrings.every((v) => { return !isNaN(parseFloat(v)) }) if (isnum) { const valuesNum: number[] = valueStrings.map((v) => parseFloat(v)) const trace: plotly.Data[] = [ { type: "scatter", x: valuesNum, y: objectiveValues, mode: "markers", }, ] layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", gridwidth: 1, automargin: true, // Otherwise the label is outside of the plot } plotly.react(plotDomId, trace, layout) } else { const vocabSet = new Set(valueStrings) const vocabArr = Array.from(vocabSet) const valuesCategorical: number[] = valueStrings.map((v) => vocabArr.findIndex((vocab) => v === vocab) ) const tickvals: number[] = vocabArr.map((v, i) => i) const trace: plotly.Data[] = [ { type: "scatter", x: valuesCategorical, y: objectiveValues, mode: "markers", }, ] layout["xaxis"] = { title: selected, type: logXScale ? "log" : "linear", gridwidth: 1, tickvals: tickvals, ticktext: vocabArr, automargin: true, // Otherwise the label is outside of the plot } plotly.react(plotDomId, trace, layout) } }