import { useState } from "react"; import ReactMarkdown from "react-markdown"; import { InputField, Tabs, Dropdown, Button, Radio, Card } from "@elvis/ui"; import { SUPPORTED_MODES, SUPPORTED_ALGORITHMS, type SettingsUi, type TrajectoryValues } from "./types.ts"; import usageMarkdown from "./usage.md?raw"; 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-3" }, "Newton": {} } const UNIVARIATE_FUNCTION_OPTIONS = { "--Custom--": "x^2", "Quadratic": "x^2", "Local Minima": "x^2 - 0.1cos(20x)", "Plateau": "x^4", } const BIVARIATE_FUNCTION_OPTIONS = { "--Custom--": "x^2 + 3y^2", "Quadratic": "x^2 + 3y^2", "Small": "(0.05x)^2 + 3(0.05y)^2", "Ackley": "-20exp(-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 - 10cos(2 pi x)) + (y^2 - 10cos(2 pi y))", "Rosenbrock": "(1 - x)^2 + 100(y - x^2)^2", } interface SidebarProps { settings: SettingsUi, setSettings: (settings: SettingsUi) => void, onRandomInitialPoint: () => void, trajectoryValues?: TrajectoryValues | null, onReset?: () => void, onNextStep?: () => void, onPrevStep?: () => void, onPlay?: () => void, onPause?: () => void, } export default function Sidebar({ settings, setSettings, onRandomInitialPoint, trajectoryValues, onReset, onNextStep, onPrevStep, // onPlay, // onPause, }: SidebarProps) { const tabs = ["Settings", "Optimize", "Usage"] 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("--Custom--"); 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 = "--Custom--"; 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); } function getLastValue(values?: T[] | null): T | null { return values && values.length > 0 ? values[values.length - 1] : null; } const currentX = getLastValue(trajectoryValues?.x); const currentY = getLastValue(trajectoryValues?.y); // univariate only const currentDerivative = getLastValue(trajectoryValues?.derivative); const currentSecondDerivative = getLastValue(trajectoryValues?.secondDerivative); // bivariate only const currentZ = getLastValue(trajectoryValues?.z); const currentGradient = getLastValue(trajectoryValues?.gradient); const currentHessian = getLastValue(trajectoryValues?.hessian); return ( {/* Tab content */}
{activeTab === "Settings" && ( <> updateSettings("functionExpr", value)} readonly={functionOption !== "--Custom--"} rows={3} /> updateSettings("algorithm", value)} />
updateSettings("x0", value)} /> {settings.mode === "Bivariate" && ( updateSettings("y0", value)} /> )}
{/* todo button for random init */}