mirror of
https://github.com/wassname/optuna-dashboard.git
synced 2026-09-11 12:30:25 +08:00
Merge pull request #778 from knshnb/analytics-plot-backend
Analytics plot backend
This commit is contained in:
@@ -271,6 +271,18 @@ def create_app(
|
||||
)
|
||||
if plot_type == "contour":
|
||||
fig = optuna.visualization.plot_contour(study)
|
||||
elif plot_type == "slice":
|
||||
fig = optuna.visualization.plot_slice(study)
|
||||
# Note: Optuna's implementation forces a minimum width.
|
||||
# We override it to prevent the figure from going beyond the screen width.
|
||||
# https://github.com/optuna/optuna/blob/2abd0ae81eaf3683ce1dd580429904c8a705300d/optuna/visualization/_slice.py#L237-L239
|
||||
fig.update_layout(width=None)
|
||||
elif plot_type == "parallel_coordinate":
|
||||
fig = optuna.visualization.plot_parallel_coordinate(study)
|
||||
elif plot_type == "rank":
|
||||
fig = optuna.visualization.plot_rank(study)
|
||||
elif plot_type == "edf":
|
||||
fig = optuna.visualization.plot_edf(study)
|
||||
else:
|
||||
response.status = 404 # Not found
|
||||
return {"reason": f"plot_type={plot_type} is not supported."}
|
||||
|
||||
@@ -448,6 +448,10 @@ type PlotResponse = {
|
||||
}
|
||||
export enum PlotType {
|
||||
Contour = "contour",
|
||||
Slice = "slice",
|
||||
ParallelCoordinate = "parallel_coordinate",
|
||||
Rank = "rank",
|
||||
EDF = "edf",
|
||||
}
|
||||
export const getPlotAPI = (
|
||||
studyId: number,
|
||||
|
||||
@@ -15,27 +15,16 @@ import blue from "@mui/material/colors/blue"
|
||||
import { plotlyDarkTemplate } from "./PlotlyDarkMode"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { getAxisInfo } from "../graphUtil"
|
||||
import { useQuery } from "../urlQuery"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { useRecoilValue } from "recoil"
|
||||
import { plotlypyIsAvailableState } from "../state"
|
||||
import { useBackendRender } from "../state"
|
||||
|
||||
const plotDomId = "graph-contour"
|
||||
|
||||
export const Contour: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const query = useQuery()
|
||||
const plotlypyIsAvailable = useRecoilValue<boolean>(plotlypyIsAvailableState)
|
||||
if (query.get("plotlypy_rendering") === "true") {
|
||||
if (plotlypyIsAvailable) {
|
||||
return <ContourBackend study={study} />
|
||||
} else {
|
||||
console.warn(
|
||||
"Use frontend rendering because plotlypy is specified but not available."
|
||||
)
|
||||
return <ContourFrontend study={study} />
|
||||
}
|
||||
if (useBackendRender()) {
|
||||
return <ContourBackend study={study} />
|
||||
} else {
|
||||
return <ContourFrontend study={study} />
|
||||
}
|
||||
|
||||
@@ -17,6 +17,8 @@ import {
|
||||
useParamTargets,
|
||||
} from "../trialFilter"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { useBackendRender } from "../state"
|
||||
|
||||
const plotDomId = "graph-parallel-coordinate"
|
||||
|
||||
@@ -86,6 +88,35 @@ const useTargets = (
|
||||
|
||||
export const GraphParallelCoordinate: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
if (useBackendRender()) {
|
||||
return <GraphParallelCoordinateBackend study={study} />
|
||||
} else {
|
||||
return <GraphParallelCoordinateFrontend study={study} />
|
||||
}
|
||||
}
|
||||
|
||||
const GraphParallelCoordinateBackend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const studyId = study?.id
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.ParallelCoordinate)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId])
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
const GraphParallelCoordinateFrontend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const theme = useTheme()
|
||||
const [targets, searchSpace, renderCheckBoxes] = useTargets(study)
|
||||
|
||||
@@ -14,6 +14,8 @@ import {
|
||||
import { plotlyDarkTemplate } from "./PlotlyDarkMode"
|
||||
import { getAxisInfo, makeHovertext } from "../graphUtil"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { useBackendRender } from "../state"
|
||||
|
||||
const plotDomId = "graph-rank"
|
||||
|
||||
@@ -31,6 +33,35 @@ interface RankPlotInfo {
|
||||
|
||||
export const GraphRank: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
if (useBackendRender()) {
|
||||
return <GraphRankBackend study={study} />
|
||||
} else {
|
||||
return <GraphRankFrontend study={study} />
|
||||
}
|
||||
}
|
||||
|
||||
const GraphRankBackend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const studyId = study?.id
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.Rank)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId])
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
const GraphRankFrontend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const theme = useTheme()
|
||||
const [objectiveId, setobjectiveId] = useState<number>(0)
|
||||
|
||||
@@ -20,6 +20,8 @@ import {
|
||||
useParamTargets,
|
||||
} from "../trialFilter"
|
||||
import { useMergedUnionSearchSpace } from "../searchSpace"
|
||||
import { getPlotAPI, PlotType } from "../apiClient"
|
||||
import { useBackendRender } from "../state"
|
||||
|
||||
const plotDomId = "graph-slice"
|
||||
|
||||
@@ -32,6 +34,35 @@ const isLogScale = (s: SearchSpaceItem): boolean => {
|
||||
|
||||
export const GraphSlice: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
if (useBackendRender()) {
|
||||
return <GraphSliceBackend study={study} />
|
||||
} else {
|
||||
return <GraphSliceFrontend study={study} />
|
||||
}
|
||||
}
|
||||
|
||||
const GraphSliceBackend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const studyId = study?.id
|
||||
useEffect(() => {
|
||||
if (studyId === undefined) {
|
||||
return
|
||||
}
|
||||
getPlotAPI(studyId, PlotType.Slice)
|
||||
.then(({ data, layout }) => {
|
||||
plotly.react(plotDomId, data, layout)
|
||||
})
|
||||
.catch((err) => {
|
||||
console.error(err)
|
||||
})
|
||||
}, [studyId])
|
||||
return <Box id={plotDomId} sx={{ height: "450px" }} />
|
||||
}
|
||||
|
||||
const GraphSliceFrontend: FC<{
|
||||
study: StudyDetail | null
|
||||
}> = ({ study = null }) => {
|
||||
const theme = useTheme()
|
||||
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { atom, useRecoilValue } from "recoil"
|
||||
import { useQuery } from "./urlQuery"
|
||||
|
||||
export const studySummariesState = atom<StudySummary[]>({
|
||||
key: "studySummaries",
|
||||
@@ -104,3 +105,18 @@ export const useArtifacts = (studyId: number, trialId: number): Artifact[] => {
|
||||
}
|
||||
return trial.artifacts
|
||||
}
|
||||
|
||||
export const useBackendRender = (): boolean => {
|
||||
const query = useQuery()
|
||||
const plotlypyIsAvailable = useRecoilValue<boolean>(plotlypyIsAvailableState)
|
||||
|
||||
if (query.get("plotlypy_rendering") === "true") {
|
||||
if (plotlypyIsAvailable) {
|
||||
return true
|
||||
}
|
||||
console.warn(
|
||||
"Use frontend rendering because plotlypy is specified but not available."
|
||||
)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user