diff --git a/package.json b/package.json
index 4785f085..9c214ab9 100644
--- a/package.json
+++ b/package.json
@@ -5,7 +5,7 @@
"description": "Dashboard for Optuna",
"main": "index.js",
"scripts": {
- "fmt": "biome format --write .",
+ "fmt": "biome format --write . && biome check standalone_app --apply",
"lint": "npm run lint:eslint && npm run lint:biome",
"lint:eslint": "eslint . --ext .ts,.tsx --max-warnings 0",
"lint:biome": "biome format . && biome ci standalone_app",
diff --git a/standalone_app/package-lock.json b/standalone_app/package-lock.json
index 1c9a76a9..b105f5bf 100644
--- a/standalone_app/package-lock.json
+++ b/standalone_app/package-lock.json
@@ -20,8 +20,7 @@
"plotly.js-dist-min": "^2.30.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-router-dom": "^6.22.2",
- "recoil": "^0.7.7"
+ "react-router-dom": "^6.22.3"
},
"devDependencies": {
"@types/plotly.js": "^2.29.2",
@@ -1325,9 +1324,9 @@
}
},
"node_modules/@remix-run/router": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
- "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q==",
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
+ "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==",
"engines": {
"node": ">=14.0.0"
}
@@ -2577,11 +2576,6 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
- "node_modules/hamt_plus": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
- "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
- },
"node_modules/has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -3165,11 +3159,11 @@
}
},
"node_modules/react-router": {
- "version": "6.22.2",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
- "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
+ "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"dependencies": {
- "@remix-run/router": "1.15.2"
+ "@remix-run/router": "1.15.3"
},
"engines": {
"node": ">=14.0.0"
@@ -3179,12 +3173,12 @@
}
},
"node_modules/react-router-dom": {
- "version": "6.22.2",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
- "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
+ "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"dependencies": {
- "@remix-run/router": "1.15.2",
- "react-router": "6.22.2"
+ "@remix-run/router": "1.15.3",
+ "react-router": "6.22.3"
},
"engines": {
"node": ">=14.0.0"
@@ -3221,25 +3215,6 @@
"node": ">= 10.13.0"
}
},
- "node_modules/recoil": {
- "version": "0.7.7",
- "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
- "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
- "dependencies": {
- "hamt_plus": "1.0.2"
- },
- "peerDependencies": {
- "react": ">=16.13.1"
- },
- "peerDependenciesMeta": {
- "react-dom": {
- "optional": true
- },
- "react-native": {
- "optional": true
- }
- }
- },
"node_modules/regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
@@ -4638,9 +4613,9 @@
"integrity": "sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A=="
},
"@remix-run/router": {
- "version": "1.15.2",
- "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.2.tgz",
- "integrity": "sha512-+Rnav+CaoTE5QJc4Jcwh5toUpnVLKYbpU6Ys0zqbakqbaLQHeglLVHPfxOiQqdNmUy5C2lXz5dwC6tQNX2JW2Q=="
+ "version": "1.15.3",
+ "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz",
+ "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w=="
},
"@rollup/rollup-android-arm-eabi": {
"version": "4.12.1",
@@ -5586,11 +5561,6 @@
"integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
"dev": true
},
- "hamt_plus": {
- "version": "1.0.2",
- "resolved": "https://registry.npmjs.org/hamt_plus/-/hamt_plus-1.0.2.tgz",
- "integrity": "sha512-t2JXKaehnMb9paaYA7J0BX8QQAY8lwfQ9Gjf4pg/mk4krt+cmwmU652HOoWonf+7+EQV97ARPMhhVgU1ra2GhA=="
- },
"has": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz",
@@ -6012,20 +5982,20 @@
"dev": true
},
"react-router": {
- "version": "6.22.2",
- "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.2.tgz",
- "integrity": "sha512-YD3Dzprzpcq+tBMHBS822tCjnWD3iIZbTeSXMY9LPSG541EfoBGyZ3bS25KEnaZjLcmQpw2AVLkFyfgXY8uvcw==",
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz",
+ "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==",
"requires": {
- "@remix-run/router": "1.15.2"
+ "@remix-run/router": "1.15.3"
}
},
"react-router-dom": {
- "version": "6.22.2",
- "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.2.tgz",
- "integrity": "sha512-WgqxD2qySEIBPZ3w0sHH+PUAiamDeszls9tzqMPBDA1YYVucTBXLU7+gtRfcSnhe92A3glPnvSxK2dhNoAVOIQ==",
+ "version": "6.22.3",
+ "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz",
+ "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==",
"requires": {
- "@remix-run/router": "1.15.2",
- "react-router": "6.22.2"
+ "@remix-run/router": "1.15.3",
+ "react-router": "6.22.3"
}
},
"react-transition-group": {
@@ -6048,14 +6018,6 @@
"resolve": "^1.20.0"
}
},
- "recoil": {
- "version": "0.7.7",
- "resolved": "https://registry.npmjs.org/recoil/-/recoil-0.7.7.tgz",
- "integrity": "sha512-8Og5KPQW9LwC577Vc7Ug2P0vQshkv1y3zG3tSSkWMqkWSwHmE+by06L8JtnGocjW6gcCvfwB3YtrJG6/tWivNQ==",
- "requires": {
- "hamt_plus": "1.0.2"
- }
- },
"regenerator-runtime": {
"version": "0.14.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
diff --git a/standalone_app/package.json b/standalone_app/package.json
index c3c471e9..b59aabd0 100644
--- a/standalone_app/package.json
+++ b/standalone_app/package.json
@@ -32,7 +32,6 @@
"plotly.js-dist-min": "^2.30.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
- "react-router-dom": "^6.22.2",
- "recoil": "^0.7.7"
+ "react-router-dom": "^6.22.3"
}
}
diff --git a/standalone_app/src/browser_app_entry.tsx b/standalone_app/src/browser_app_entry.tsx
index 705200cc..f2d2b0f1 100644
--- a/standalone_app/src/browser_app_entry.tsx
+++ b/standalone_app/src/browser_app_entry.tsx
@@ -1,13 +1,13 @@
import React from "react"
import ReactDOM from "react-dom/client"
-import { RecoilRoot } from "recoil"
import { App } from "./components/App"
+import { StorageProvider } from "./components/StorageProvider"
import "./index.css"
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
-
+
-
+
)
diff --git a/standalone_app/src/components/StorageLoader.tsx b/standalone_app/src/components/StorageLoader.tsx
index 2b3640d6..fd108976 100644
--- a/standalone_app/src/components/StorageLoader.tsx
+++ b/standalone_app/src/components/StorageLoader.tsx
@@ -13,17 +13,15 @@ import React, {
MouseEventHandler,
useRef,
useState,
+ useContext,
} from "react"
-import { useSetRecoilState } from "recoil"
-import { loadJournalStorage } from "../journalStorage"
-import { loadSQLite3Storage } from "../sqlite3"
-import { studiesState } from "../state"
+import { StorageContext, getStorage } from "./StorageProvider"
export const StorageLoader: FC = () => {
const theme = useTheme()
const [dragOver, setDragOver] = useState(false)
+ const { setStorage } = useContext(StorageContext)
- const setStudies = useSetRecoilState(studiesState)
const inputRef = useRef(null)
const loadStorageFromFile = (file: File): void => {
@@ -31,13 +29,8 @@ export const StorageLoader: FC = () => {
r.addEventListener("load", () => {
const arrayBuffer = r.result as ArrayBuffer | null
if (arrayBuffer !== null) {
- const header = new Uint8Array(arrayBuffer, 0, 16)
- const headerString = new TextDecoder().decode(header)
- if (headerString === "SQLite format 3\u0000") {
- loadSQLite3Storage(arrayBuffer, setStudies)
- } else {
- loadJournalStorage(arrayBuffer, setStudies)
- }
+ const s = getStorage(arrayBuffer)
+ setStorage(s)
}
})
r.readAsArrayBuffer(file)
diff --git a/standalone_app/src/components/StorageProvider.tsx b/standalone_app/src/components/StorageProvider.tsx
new file mode 100644
index 00000000..1ac3c23b
--- /dev/null
+++ b/standalone_app/src/components/StorageProvider.tsx
@@ -0,0 +1,31 @@
+import React, { FC, createContext, useState } from "react"
+import { JournalFileStorage } from "../journalStorage"
+import { SQLite3Storage } from "../sqlite3"
+
+export const StorageContext = createContext<{
+ storage: OptunaStorage | null
+ setStorage: (storage: OptunaStorage) => void
+}>({
+ storage: null,
+ setStorage: () => {},
+})
+
+export const getStorage = (arrayBuffer: ArrayBuffer): OptunaStorage => {
+ const header = new Uint8Array(arrayBuffer, 0, 16)
+ const headerString = new TextDecoder().decode(header)
+ if (headerString === "SQLite format 3\u0000") {
+ return new SQLite3Storage(arrayBuffer)
+ }
+ return new JournalFileStorage(arrayBuffer)
+}
+
+export const StorageProvider: FC<{
+ children: React.ReactNode
+}> = ({ children }) => {
+ const [storage, setStorage] = useState(null)
+ return (
+
+ {children}
+
+ )
+}
diff --git a/standalone_app/src/components/StudyDetail.tsx b/standalone_app/src/components/StudyDetail.tsx
index ad8bdfc0..2cb0baf6 100644
--- a/standalone_app/src/components/StudyDetail.tsx
+++ b/standalone_app/src/components/StudyDetail.tsx
@@ -13,30 +13,36 @@ import {
useTheme,
} from "@mui/material"
import Grid2 from "@mui/material/Unstable_Grid2"
-import React, { FC } from "react"
+import React, { FC, useContext, useState, useEffect } from "react"
import { Link, useParams } from "react-router-dom"
-import { useRecoilValue } from "recoil"
-import { studiesState } from "../state"
import { PlotHistory } from "./PlotHistory"
import { PlotImportance } from "./PlotImportance"
import { PlotIntermediateValues } from "./PlotIntermediateValues"
+import { StorageContext } from "./StorageProvider"
import { TrialTable } from "./TrialTable"
-const useStudyValue = (idx: number): Study | null => {
- const studies = useRecoilValue(studiesState)
- return studies[idx] || null
-}
-
export const StudyDetail: FC<{
toggleColorMode: () => void
}> = ({ toggleColorMode }) => {
const theme = useTheme()
const { idx } = useParams<{ idx: string }>()
const idxNumber = parseInt(idx || "", 10)
- const study = useStudyValue(idxNumber)
+
+ const { storage } = useContext(StorageContext)
+ const [study, setStudy] = useState(null)
+ useEffect(() => {
+ const fetchStudy = async () => {
+ if (storage === null) {
+ return
+ }
+ const study = await storage.getStudy(idxNumber)
+ setStudy(study)
+ }
+ fetchStudy()
+ }, [storage, idxNumber])
return (
-
+ >
)
}
diff --git a/standalone_app/src/components/StudyList.tsx b/standalone_app/src/components/StudyList.tsx
index a8c9bca4..d90717c9 100644
--- a/standalone_app/src/components/StudyList.tsx
+++ b/standalone_app/src/components/StudyList.tsx
@@ -19,23 +19,40 @@ import {
useTheme,
} from "@mui/material"
import { styled } from "@mui/system"
-import React, { FC, useState, useMemo, useDeferredValue } from "react"
+import React, {
+ FC,
+ useEffect,
+ useContext,
+ useState,
+ useMemo,
+ useDeferredValue,
+} from "react"
import { Link } from "react-router-dom"
-import { useRecoilValue } from "recoil"
-import { studiesState } from "../state"
import { StorageLoader } from "./StorageLoader"
+import { StorageContext } from "./StorageProvider"
export const StudyList: FC<{
toggleColorMode: () => void
}> = ({ toggleColorMode }) => {
const theme = useTheme()
- const studies = useRecoilValue(studiesState)
+ const { storage } = useContext(StorageContext)
+ const [studies, setStudies] = useState([])
const [_studyFilterText, setStudyFilterText] = useState("")
const [sortBy, setSortBy] = useState<"id-asc" | "id-desc">("id-asc")
const studyFilterText = useDeferredValue(_studyFilterText)
+ useEffect(() => {
+ const fetchStudies = async () => {
+ if (storage === null) {
+ return
+ }
+ const studies = await storage.getStudies()
+ setStudies(studies)
+ }
+ fetchStudies()
+ }, [storage])
const filteredStudies = useMemo(() => {
- const studyFilter = (row: Study): boolean => {
+ const studyFilter = (row: StudySummary): boolean => {
const keywords = studyFilterText.split(" ")
return !keywords.every((k) => {
if (k === "") {
@@ -44,7 +61,7 @@ export const StudyList: FC<{
return row.study_name.indexOf(k) >= 0
})
}
- let filteredStudies: Study[] = studies.filter((s) => !studyFilter(s))
+ let filteredStudies: StudySummary[] = studies.filter((s) => !studyFilter(s))
if (sortBy === "id-desc") {
filteredStudies = filteredStudies.reverse()
}
diff --git a/standalone_app/src/journalStorage.ts b/standalone_app/src/journalStorage.ts
index e2fcd83c..8de7af61 100644
--- a/standalone_app/src/journalStorage.ts
+++ b/standalone_app/src/journalStorage.ts
@@ -1,5 +1,3 @@
-import { SetterOrUpdater } from "recoil"
-
// JournalStorage
enum JournalOperation {
CREATE_STUDY = 0,
@@ -329,10 +327,20 @@ class JournalStorage {
}
}
-export const loadJournalStorage = (
- arrayBuffer: ArrayBuffer,
- setter: SetterOrUpdater
-): void => {
+export class JournalFileStorage implements OptunaStorage {
+ studies: Study[]
+ constructor(arrayBuffer: ArrayBuffer) {
+ this.studies = loadJournalStorage(arrayBuffer)
+ }
+ getStudies = async (): Promise => {
+ return this.studies
+ }
+ getStudy = async (idx: number): Promise => {
+ return this.studies[idx] || null
+ }
+}
+
+export const loadJournalStorage = (arrayBuffer: ArrayBuffer): Study[] => {
const decoder = new TextDecoder("utf-8")
const logs = decoder.decode(arrayBuffer).split("\n")
@@ -383,6 +391,5 @@ export const loadJournalStorage = (
}
}
- const studies = journalStorage.getStudies()
- setter((prev) => [...prev, ...studies])
+ return journalStorage.getStudies()
}
diff --git a/standalone_app/src/sqlite3.ts b/standalone_app/src/sqlite3.ts
index aac99150..2a8261c5 100644
--- a/standalone_app/src/sqlite3.ts
+++ b/standalone_app/src/sqlite3.ts
@@ -1,6 +1,5 @@
// @ts-ignore
import sqlite3InitModule from "@sqlite.org/sqlite-wasm"
-import { SetterOrUpdater } from "recoil"
type SQLite3DB = {
exec(options: {
@@ -10,44 +9,57 @@ type SQLite3DB = {
}): void
}
-export const loadSQLite3Storage = (
- arrayBuffer: ArrayBuffer,
- setter: SetterOrUpdater
-): void => {
- sqlite3InitModule({
- // biome-ignore lint/suspicious/noExplicitAny:
- print: (...args: any): void => {
- console.log(args)
- },
- // biome-ignore lint/suspicious/noExplicitAny:
- printErr: (...args: any): void => {
- console.log(args)
- },
- // @ts-ignore
- }).then((sqlite3) => {
- const p = sqlite3.wasm.allocFromTypedArray(arrayBuffer)
- const db = new sqlite3.oo1.DB()
- const rc = sqlite3.capi.sqlite3_deserialize(
+export class SQLite3Storage implements OptunaStorage {
+ db: Promise
+ summaries_cache: StudySummary[] | null
+ constructor(arrayBuffer: ArrayBuffer) {
+ this.db = this.initDB(arrayBuffer)
+ this.summaries_cache = null
+ }
+
+ async initDB(arrayBuffer: ArrayBuffer): Promise {
+ return sqlite3InitModule({
+ print: console.log,
+ printErr: console.log,
// @ts-ignore
- db.pointer,
- "main",
- p,
- arrayBuffer.byteLength,
- arrayBuffer.byteLength,
- sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE
- )
- db.checkRc(rc)
- try {
- const schemaVersion = getSchemaVersion(db)
- if (!isSupportedSchema(schemaVersion)) {
- return
- }
- const studies = getStudies(db, schemaVersion)
- setter((prev) => [...prev, ...studies])
- } finally {
- db.close()
+ }).then((sqlite3) => {
+ const p = sqlite3.wasm.allocFromTypedArray(arrayBuffer)
+ const db = new sqlite3.oo1.DB()
+ const rc = sqlite3.capi.sqlite3_deserialize(
+ // @ts-ignore
+ db.pointer,
+ "main",
+ p,
+ arrayBuffer.byteLength,
+ arrayBuffer.byteLength,
+ sqlite3.capi.SQLITE_DESERIALIZE_FREEONCLOSE
+ )
+ db.checkRc(rc)
+ return db
+ })
+ }
+
+ getStudies = async (): Promise => {
+ const db = await this.db
+ this.summaries_cache = getStudySummaries(db)
+ return this.summaries_cache
+ }
+
+ getStudy = async (idx: number): Promise => {
+ const db = await this.db
+ const schemaVersion = getSchemaVersion(db)
+ if (!isSupportedSchema(schemaVersion)) {
+ return null
}
- })
+ if (this.summaries_cache === null) {
+ this.summaries_cache = getStudySummaries(db)
+ }
+ const summary = this.summaries_cache[idx]
+ if (summary === undefined) {
+ return null
+ }
+ return getStudy(db, schemaVersion, summary)
+ }
}
const getSchemaVersion = (db: SQLite3DB): string => {
@@ -84,8 +96,8 @@ const isGreaterSchemaVersion = (
return left > right
}
-const getStudies = (db: SQLite3DB, schemaVersion: string): Study[] => {
- const studies: Study[] = []
+const getStudySummaries = (db: SQLite3DB): StudySummary[] => {
+ const summaries: StudySummary[] = []
db.exec({
sql:
"SELECT s.study_id, s.study_name, sd.direction, sd.objective" +
@@ -99,64 +111,73 @@ const getStudies = (db: SQLite3DB, schemaVersion: string): Study[] => {
vals[2] === "MINIMIZE" ? "minimize" : "maximize"
const objective = vals[3]
- const trials = getTrials(db, studyId, schemaVersion)
- const union_search_space: SearchSpaceItem[] = []
- const union_user_attrs: AttributeSpec[] = []
- let intersection_search_space: Set = new Set()
-
- for (const trial of trials) {
- const userAttrs = getTrialUserAttributes(db, trial.trial_id)
- for (const attr of userAttrs) {
- if (union_user_attrs.findIndex((s) => s.key === attr.key) === -1) {
- union_user_attrs.push({ key: attr.key, sortable: false })
- }
- }
-
- const params = getTrialParams(db, trial.trial_id)
- const param_names = new Set()
- for (const param of params) {
- param_names.add(param.name)
- if (
- union_search_space.findIndex((s) => s.name === param.name) === -1
- ) {
- union_search_space.push({ name: param.name })
- }
- }
- if (intersection_search_space.size === 0) {
- // biome-ignore lint/complexity/noForEach:
- param_names.forEach((s) => {
- intersection_search_space.add({
- name: s,
- })
- })
- } else {
- intersection_search_space = new Set(
- Array.from(intersection_search_space).filter((s) =>
- param_names.has(s.name)
- )
- )
- }
- trial.params = params
- trial.user_attrs = userAttrs
- }
-
if (objective === 0) {
- studies.push({
+ summaries.push({
study_id: studyId,
study_name: studyName,
directions: [direction],
- union_search_space: union_search_space,
- intersection_search_space: Array.from(intersection_search_space),
- union_user_attrs: union_user_attrs,
- trials: trials,
})
return
}
- const index = studies.findIndex((s) => s.study_id === studyId)
- studies[index].directions.push(direction)
+ const index = summaries.findIndex((s) => s.study_id === studyId)
+ summaries[index].directions.push(direction)
},
})
- return studies
+ return summaries
+}
+
+const getStudy = (
+ db: SQLite3DB,
+ schemaVersion: string,
+ summary: StudySummary
+): Study => {
+ const study: Study = {
+ study_id: summary.study_id,
+ study_name: summary.study_name,
+ directions: summary.directions,
+ union_search_space: [],
+ intersection_search_space: [],
+ union_user_attrs: [],
+ trials: [],
+ }
+
+ let intersection_search_space: Set = new Set()
+ study.trials = getTrials(db, summary.study_id, schemaVersion)
+ for (const trial of study.trials) {
+ const userAttrs = getTrialUserAttributes(db, trial.trial_id)
+ for (const attr of userAttrs) {
+ if (study.union_user_attrs.findIndex((s) => s.key === attr.key) === -1) {
+ study.union_user_attrs.push({ key: attr.key, sortable: false })
+ }
+ }
+
+ const params = getTrialParams(db, trial.trial_id)
+ const param_names = new Set()
+ for (const param of params) {
+ param_names.add(param.name)
+ if (
+ study.union_search_space.findIndex((s) => s.name === param.name) === -1
+ ) {
+ study.union_search_space.push({ name: param.name })
+ }
+ }
+ if (intersection_search_space.size === 0) {
+ // biome-ignore lint/complexity/noForEach:
+ param_names.forEach((s) => {
+ intersection_search_space.add({ name: s })
+ })
+ } else {
+ intersection_search_space = new Set(
+ Array.from(intersection_search_space).filter((s) =>
+ param_names.has(s.name)
+ )
+ )
+ }
+ trial.params = params
+ trial.user_attrs = userAttrs
+ }
+ study.intersection_search_space = Array.from(intersection_search_space)
+ return study
}
const getTrials = (
diff --git a/standalone_app/src/state.ts b/standalone_app/src/state.ts
deleted file mode 100644
index a1bcc858..00000000
--- a/standalone_app/src/state.ts
+++ /dev/null
@@ -1,6 +0,0 @@
-import { atom } from "recoil"
-
-export const studiesState = atom({
- key: "studies",
- default: [],
-})
diff --git a/standalone_app/src/types/index.d.ts b/standalone_app/src/types/index.d.ts
index 4bc34bf5..1d258f36 100644
--- a/standalone_app/src/types/index.d.ts
+++ b/standalone_app/src/types/index.d.ts
@@ -4,6 +4,11 @@ type TrialState = "Running" | "Complete" | "Pruned" | "Fail" | "Waiting"
type TrialStateFinished = "Complete" | "Fail" | "Pruned"
type StudyDirection = "maximize" | "minimize" | "not_set"
+type OptunaStorage = {
+ getStudies: () => Promise
+ getStudy: (idx: number) => Promise
+}
+
type FloatDistribution = {
type: "FloatDistribution"
low: number
@@ -46,6 +51,12 @@ type AttributeSpec = {
sortable: boolean
}
+type StudySummary = {
+ study_id: number
+ study_name: string
+ directions: StudyDirection[]
+}
+
type Study = {
study_id: number
study_name: string
diff --git a/standalone_app/src/vscode_entry.tsx b/standalone_app/src/vscode_entry.tsx
index dad1868c..66c522a5 100644
--- a/standalone_app/src/vscode_entry.tsx
+++ b/standalone_app/src/vscode_entry.tsx
@@ -1,22 +1,15 @@
-import React, { FC, useEffect } from "react"
+import React, { FC, useEffect, useContext } from "react"
import ReactDOM from "react-dom/client"
-import { RecoilRoot, SetterOrUpdater, useSetRecoilState } from "recoil"
import { App } from "./components/App"
+import {
+ StorageContext,
+ StorageProvider,
+ getStorage,
+} from "./components/StorageProvider"
import "./index.css"
-import { loadJournalStorage } from "./journalStorage"
-import { loadSQLite3Storage } from "./sqlite3"
-import { studiesState } from "./state"
export const AppWrapper: FC = () => {
- const setStudies = useSetRecoilState(studiesState)
-
- // TODO(c-bata): Fix the type annotation
- const onceSetStudies: SetterOrUpdater = (
- setter: (currVal: Study[]) => Study[]
- ): void => {
- const studies = setter([])
- setStudies(studies)
- }
+ const { setStorage } = useContext(StorageContext)
// biome-ignore lint/correctness/useExhaustiveDependencies:
useEffect(() => {
@@ -27,8 +20,6 @@ export const AppWrapper: FC = () => {
let len: number
let bytes: Uint8Array
let arrayBuffer: ArrayBuffer
- let header: Uint8Array
- let headerString: string
switch (message.type) {
case "optunaStorage":
@@ -40,13 +31,7 @@ export const AppWrapper: FC = () => {
bytes[i] = binaryString.charCodeAt(i)
}
arrayBuffer = bytes.buffer
- header = new Uint8Array(arrayBuffer, 0, 16)
- headerString = new TextDecoder().decode(header)
- if (headerString === "SQLite format 3\u0000") {
- loadSQLite3Storage(arrayBuffer, onceSetStudies)
- } else {
- loadJournalStorage(arrayBuffer, onceSetStudies)
- }
+ setStorage(getStorage(arrayBuffer))
break
}
})
@@ -56,8 +41,8 @@ export const AppWrapper: FC = () => {
ReactDOM.createRoot(document.getElementById("root") as HTMLElement).render(
-
+
-
+
)