Spaces:
Running
Running
| import { useState } from "react"; | |
| import InputField from "./ui/InputField.tsx"; | |
| import Tabs from "./ui/Tabs.tsx"; | |
| import Dropdown from "./ui/Dropdown.tsx"; | |
| import Button from "./ui/Button.tsx"; | |
| import Radio from "./ui/Radio.tsx"; | |
| import { SUPPORTED_MODES, SUPPORTED_ALGORITHMS, type SettingsUi } from "./types.ts"; | |
| const DEFAULT_HYPERPARAMETERS = { | |
| "Gradient Descent": { learningRate: "0.1", momentum: "0.0" }, | |
| "Nesterov": { learningRate: "0.1", momentum: "0.0" }, | |
| "Adam": { learningRate: "0.1", beta1: "0.9", beta2: "0.999", epsilon: "1e-8" }, | |
| "Adagrad": { learningRate: "0.1", epsilon: "1e-8" }, | |
| "RMSProp": { learningRate: "0.1", beta: "0.9", epsilon: "1e-8" }, | |
| "Adadelta": { beta: "0.9", epsilon: "1e-8" }, | |
| "Newton": {} | |
| } | |
| const UNIVARIATE_FUNCTION_OPTIONS = { | |
| "--Custom--": "", | |
| "Quadratic": "x^2", | |
| "Cubic": "x^3 - 3*x^2 + 2*x", | |
| "Quartic": "x^4 - 4*x^3 + 6*x^2 - 4*x + 1", | |
| "Sine": "sin(x)", | |
| "Exponential": "exp(x) - 5", | |
| } | |
| const BIVARIATE_FUNCTION_OPTIONS = { | |
| "--Custom--": "", | |
| "Quadratic": "x^2 + y^2", | |
| "Ackley": "-20 * exp(-0.2 * sqrt(0.5 * (x^2 + y^2))) - exp(0.5 * (cos(2 * pi * x) + cos(2 * pi * y))) + e + 20", | |
| "Rasteringin": "20 + (x^2 - 10 * cos(2 * pi * x)) + (y^2 - 10 * cos(2 * pi * y))", | |
| "Rosenbrock": "(1 - x)^2 + 100 * (y - x^2)^2", | |
| } | |
| interface SidebarProps { | |
| settings: SettingsUi, | |
| setSettings: (settings: SettingsUi) => void, | |
| onReset?: () => void, | |
| onNextStep?: () => void, | |
| onPrevStep?: () => void, | |
| onPlay?: () => void, | |
| onPause?: () => void, | |
| } | |
| export default function Sidebar({ | |
| settings, | |
| setSettings, | |
| onReset, | |
| onNextStep, | |
| onPrevStep, | |
| // onPlay, | |
| // onPause, | |
| }: SidebarProps) { | |
| const tabs = ["Settings", "Optimize"] as const; | |
| const [activeTab, setActiveTab] = useState<(typeof tabs)[number]>("Settings"); | |
| function updateSettings(key: keyof SettingsUi, value: string) { | |
| if (key === "algorithm") { | |
| const defaults = DEFAULT_HYPERPARAMETERS[value as keyof typeof DEFAULT_HYPERPARAMETERS]; | |
| setSettings({ ...settings, algorithm: value as SettingsUi["algorithm"], ...defaults }); | |
| } else { | |
| setSettings({ ...settings, [key]: value }); | |
| } | |
| } | |
| const [functionOption, setFunctionOption] = useState<string>("Quadratic"); | |
| function handleFunctionOptionChange(option: string) { | |
| setFunctionOption(option); | |
| const expr = settings.mode === "Bivariate" ? BIVARIATE_FUNCTION_OPTIONS[option as keyof typeof BIVARIATE_FUNCTION_OPTIONS] : UNIVARIATE_FUNCTION_OPTIONS[option as keyof typeof UNIVARIATE_FUNCTION_OPTIONS]; | |
| updateSettings("functionExpr", expr); | |
| } | |
| function handleModeChange(mode: SettingsUi["mode"]) { | |
| // When changing modes, reset function to Quadratic as some options are mode-specific | |
| const newFunctionOption = "Quadratic"; | |
| const expr = mode === "Bivariate" | |
| ? BIVARIATE_FUNCTION_OPTIONS[newFunctionOption as keyof typeof BIVARIATE_FUNCTION_OPTIONS] | |
| : UNIVARIATE_FUNCTION_OPTIONS[newFunctionOption as keyof typeof UNIVARIATE_FUNCTION_OPTIONS]; | |
| // should update mode and functionExpr together as one may override the other | |
| setSettings({ ...settings, mode, functionExpr: expr }); | |
| setFunctionOption(newFunctionOption); | |
| } | |
| return ( | |
| <div className="bg-gray-100 flex flex-col h-full p-4 gap-2"> | |
| <Tabs tabs={tabs} activeTab={activeTab} onChange={setActiveTab} /> | |
| {/* Tab content */} | |
| {activeTab === "Settings" && ( | |
| <> | |
| <Radio | |
| label="Problem Type" | |
| options={SUPPORTED_MODES} | |
| activeOption={settings.mode} | |
| onChange={handleModeChange} | |
| /> | |
| <Dropdown | |
| label="Function" | |
| options={ | |
| settings.mode === "Bivariate" | |
| ? Object.keys(BIVARIATE_FUNCTION_OPTIONS) | |
| : Object.keys(UNIVARIATE_FUNCTION_OPTIONS) | |
| } | |
| activeOption={functionOption} | |
| onChange={handleFunctionOptionChange} | |
| /> | |
| {functionOption === "--Custom--" && ( | |
| <InputField | |
| label="Function Expression" | |
| value={settings.functionExpr} | |
| onChange={(value) => updateSettings("functionExpr", value)} | |
| /> | |
| )} | |
| <Dropdown | |
| label="Algorithm" | |
| options={SUPPORTED_ALGORITHMS} | |
| activeOption={settings.algorithm} | |
| onChange={(value: SettingsUi["algorithm"]) => updateSettings("algorithm", value)} | |
| /> | |
| <div className={`${settings.mode === "Bivariate" ? "grid grid-cols-2 gap-2" : ""}`}> | |
| <InputField | |
| label="Initial X" | |
| value={settings.x0} | |
| onChange={(value) => updateSettings("x0", value)} | |
| /> | |
| {settings.mode === "Bivariate" && ( | |
| <InputField | |
| label="Initial Y" | |
| value={settings.y0 || ""} | |
| onChange={(value) => updateSettings("y0", value)} | |
| /> | |
| )} | |
| </div> | |
| {/* todo button for random init */} | |
| <div className="grid grid-cols-2 gap-2"> | |
| {["Gradient Descent", "Nesterov", "Adam", "Adagrad", "RMSProp", "Adadelta"].includes(settings.algorithm) && ( | |
| <> | |
| <InputField | |
| label="Learning Rate" | |
| value={settings.learningRate} | |
| onChange={(value) => updateSettings("learningRate", value)} | |
| /> | |
| </> | |
| )} | |
| {["Gradient Descent", "Nesterov"].includes(settings.algorithm) && ( | |
| <> | |
| <InputField | |
| label="Momentum" | |
| value={settings.momentum} | |
| onChange={(value) => updateSettings("momentum", value)} | |
| /> | |
| </> | |
| )} | |
| {settings.algorithm === "Adam" && ( | |
| <> | |
| <InputField | |
| label="Beta 1" | |
| value={settings.beta1} | |
| onChange={(value) => updateSettings("beta1", value)} | |
| /> | |
| <InputField | |
| label="Beta 2" | |
| value={settings.beta2} | |
| onChange={(value) => updateSettings("beta2", value)} | |
| /> | |
| </> | |
| )} | |
| {["RMSProp", "Adadelta"].includes(settings.algorithm) && ( | |
| <> | |
| <InputField | |
| label="Beta" | |
| value={settings.beta} | |
| onChange={(value) => updateSettings("beta", value)} | |
| /> | |
| </> | |
| )} | |
| {["Adam", "Adagrad", "RMSProp", "Adadelta"].includes(settings.algorithm) && ( | |
| <> | |
| <InputField | |
| label="Epsilon" | |
| value={settings.epsilon} | |
| onChange={(value) => updateSettings("epsilon", value)} | |
| /> | |
| </> | |
| )} | |
| </div> | |
| </> | |
| )} | |
| {activeTab === "Optimize" && ( | |
| <> | |
| <div className="grid grid-cols-2 gap-2"> | |
| <Button label="Next Step" onClick={onNextStep}/ > | |
| <Button label="Previous Step" onClick={onPrevStep} /> | |
| </div> | |
| <Button label="Reset" onClick={onReset}/> | |
| {/* <div className="grid grid-cols-2 gap-2"> | |
| <Button label="Play" onClick={onPlay} /> | |
| <InputField label="Steps per second" /> | |
| </div> | |
| <div className="grid grid-cols-2 gap-2"> | |
| <Button label="Pause" onClick={onPause} /> | |
| </div> */} | |
| </> | |
| )} | |
| </div> | |
| ); | |
| } | |