optimization / frontends /react /src /AlgorithmControls.tsx
nanye's picture
support algorithm comparison
603b5b4
Raw History Blame Contribute Delete
7.02 kB
import { useEffect, useState } from "react";
import { InputField } from "@elvis/ui";
import { SUPPORTED_ALGORITHMS, type SettingsUi } from "./types";
export const DEFAULT_HYPERPARAMETERS = {
"Gradient Descent": { learningRate: "0.1", momentum: "0.0" },
"Nesterov": { learningRate: "0.1", momentum: "0.1" },
"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": {}
}
export default function AlgorithmControls({ settings, setSettings, onRandomInitialPoint, showInitialPoint = true }: {
settings: SettingsUi;
setSettings: (settings: SettingsUi) => void;
onRandomInitialPoint?: () => void;
showInitialPoint?: boolean;
}) {
function updateSettings(key: keyof SettingsUi, value: string) {
if (key === "algorithm") {
const defaults = DEFAULT_HYPERPARAMETERS[value as keyof typeof DEFAULT_HYPERPARAMETERS];
setSettings({ mode: settings.mode, functionExpr: settings.functionExpr, x0: settings.x0, y0: settings.y0, algorithm: value as SettingsUi["algorithm"], ...defaults });
} else {
setSettings({ ...settings, [key]: value });
}
}
const [initialPointInput, setInitialPointInput] = useState<string>(() =>
[settings.x0 ?? "", settings.y0 ?? ""].filter((part) => part !== "").join(", ")
);
useEffect(() => {
if (settings.mode === "Bivariate") {
setInitialPointInput([settings.x0 ?? "", settings.y0 ?? ""].filter((part) => part !== "").join(", "));
}
}, [settings.mode, settings.x0, settings.y0]);
function handleInitialPointChange(value: string) {
setInitialPointInput(value);
if (settings.mode === "Bivariate") {
const parts = value.split(",").map((part) => part.trim());
const [xValue, yValue] = parts;
setSettings({
...settings,
x0: xValue ?? "",
y0: yValue ?? settings.y0 ?? "",
});
} else {
updateSettings("x0", value);
}
}
return <>
<div className="grid grid-cols-[6rem_minmax(0,1fr)] items-center gap-0.5">
<label className="text-sm font-semibold whitespace-nowrap pr-0.25">Algorithm</label>
<div className="min-w-0">
<select
value={settings.algorithm}
onChange={(event) => updateSettings("algorithm", event.target.value as SettingsUi["algorithm"])}
className="h-10 w-full rounded border border-stone-300 bg-white px-2 py-2 text-sm focus:outline-none focus:ring-2 focus:ring-stone-400"
>
{SUPPORTED_ALGORITHMS.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</select>
</div>
</div>
{showInitialPoint && (settings.mode === "Bivariate" ? (
<div className="grid grid-cols-[6rem_minmax(0,1fr)] items-center gap-0.5">
<label className="text-sm font-medium whitespace-nowrap pr-0.25">Initial (X, Y)</label>
<div className="flex min-w-0 items-center gap-2">
<input
type="text"
value={initialPointInput}
onChange={(event) => handleInitialPointChange(event.target.value)}
className="h-10 w-full rounded border border-stone-300 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-stone-400"
/>
<button
type="button"
onClick={onRandomInitialPoint}
className="h-10 rounded border border-gray-300 bg-orange-200 px-3 py-1 text-sm text-stone-700 hover:bg-orange-300"
>
random
</button>
</div>
</div>
) : (
<div className="grid grid-cols-[6rem_minmax(0,1fr)] items-center gap-0.5">
<label className="text-sm font-medium whitespace-nowrap pr-0.25">Initial X</label>
<div className="flex min-w-0 items-center gap-2">
<input
type="text"
value={settings.x0}
onChange={(event) => updateSettings("x0", event.target.value)}
className="h-10 w-full rounded border border-stone-300 px-2 py-1 text-sm focus:outline-none focus:ring-2 focus:ring-stone-400"
/>
<button
type="button"
onClick={onRandomInitialPoint}
className="h-10 rounded border border-gray-300 bg-orange-200 px-3 py-1 text-sm text-stone-700 hover:bg-orange-300"
>
random
</button>
</div>
</div>
))}
<div className="grid grid-cols-2 gap-2">
{["Gradient Descent", "Nesterov", "Adam", "Adagrad", "RMSProp"].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>
</>;
}