File size: 7,017 Bytes
603b5b4
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
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>
  </>;
}