File size: 4,191 Bytes
b9499ab
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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
import { AppLayout, Divider, Dropdown, InputField, InputSection, SegmentedControl } from "@elvis/ui";

import ComputationGraph from "./ComputationGraph";
import EvaluationTable from "./EvaluationTable";
import MathText from "./MathText";
import RunResult from "./RunResult";
import TimelineControls from "./TimelineControls";
import type { Mode, RunSettings } from "./types";
import useAutodiff from "./useAutodiff";
import useRunResults from "./useRunResults";

const MODE_OPTIONS: { value: Mode; label: string }[] = [
  { value: "forward", label: "Forward" },
  { value: "reverse", label: "Reverse" },
];

function runCaption(run: RunSettings): string {
  const values = Object.entries(run.inputs).map(([name, value]) => `${name} = ${value}`);
  return `${values.join(", ")}${run.mode === "forward" ? `, differentiating by ${run.seed}` : ""}`;
}

export default function App() {
  const app = useAutodiff();
  const prefix = `f(${app.graph?.inputs.join(", ") ?? "x"}) =`;
  const { flow, state } = app.snapshot;
  const results = useRunResults(app.graph, app.snapshot);
  // Labels follow the run on screen, not a control just changed
  const shownMode = app.run?.mode ?? app.mode;
  const shownSeed = app.run?.mode === "forward" ? app.run.seed : app.seed;

  return (
    <AppLayout
      title="Automatic Differentiation"
      panel={
        <>
          <InputSection label="Function">
            <InputField
              label="Function"
              hideLabel
              prefix={prefix}
              value={app.expression}
              onChange={app.setExpression}
              error={app.functionError}
              mono
              rows={2}
            />
          </InputSection>
          {app.graph && (
            <>
              <Divider />
              <InputSection label="Inputs">
                <div className="grid grid-cols-2 gap-2">
                  {app.inputs.map((input) => (
                    <InputField
                      key={input.name}
                      label={`Value of ${input.name}`}
                      hideLabel
                      prefix={`${input.name} =`}
                      value={input.text}
                      onChange={(text) => app.setInput(input.name, text)}
                      error={input.value === null ? "Enter a number." : null}
                      mono
                    />
                  ))}
                </div>
                {app.runError && <p className="font-semibold text-red-600">{app.runError}</p>}
              </InputSection>
              <Divider />
              <InputSection label="Mode">
                <SegmentedControl label="Mode" options={MODE_OPTIONS} value={app.mode} onChange={app.setMode} />
                {app.mode === "forward" && app.graph.inputs.length > 1 && (
                  <Dropdown label="Differentiate by" options={app.graph.inputs} activeOption={app.seed} onChange={app.setSeed} />
                )}
              </InputSection>
              <Divider />
              <RunResult graph={app.graph} results={results} mode={shownMode} />
            </>
          )}
        </>
      }
      footer={
        <>
          {app.status === "loading" && (
            <p className="text-sm text-ink-muted">Loading Python… actions will run once it is ready.</p>
          )}
          {app.status === "failed" && <p className="font-semibold text-red-600">Python failed to load.</p>}
          <TimelineControls timeline={app.timeline} snapshot={app.snapshot} animated={app.animated} onAnimatedChange={app.setAnimated} />
        </>
      }
    >
      {app.graph && (
        <div className="flex min-h-0 flex-1 flex-col gap-4">
          <ComputationGraph graph={app.graph} state={state} flow={flow} timeline={app.timeline} />
          <Divider />
          <EvaluationTable graph={app.graph} state={state} mode={shownMode} seed={shownSeed} />
          {app.run && (
            <p className="text-sm text-ink-muted">
              {shownMode === "forward" ? "Forward" : "Reverse"} mode at <span className="font-mono"><MathText text={runCaption(app.run)} /></span>
            </p>
          )}
        </div>
      )}
    </AppLayout>
  );
}