import { useMemo } from "react";
import { formatNumber } from "./formula";
import { edgePath, layoutGraph, OUTPUT, SLOT_OFFSET, type PlacedEdge, type PlacedShape } from "./layout";
import MathText from "./MathText";
import StepFlow, { TOKEN_BASELINE } from "./StepFlow";
import type { Flow, Timeline } from "./Timeline";
import type { Graph, State } from "./types";
// Small graphs keep their size rather than filling the plot
const MIN_VIEW_WIDTH = 560;
const MIN_VIEW_HEIGHT = 240;
// The baseline of an edge's name, relative to the edge
const ABOVE_EDGE = -5;
function SlotText({ x, y, number }: { x: number; y: number; number: number }) {
return (
{formatNumber(number)}
);
}
// A shape with its name or operator in the middle, its value above and its derivative below
function Shape({ shape, current, value, derivative }: { shape: PlacedShape; current: boolean; value: number | undefined; derivative: number | undefined }) {
const fill = current ? "fill-marker-soft" : shape.kind === "op" ? "fill-white" : "fill-paper";
return (
{shape.kind === "op" ? (
) : (
)}
{value !== undefined && }
{derivative !== undefined && }
);
}
export default function ComputationGraph({
graph,
state,
flow,
timeline,
}: {
graph: Graph;
state: State | undefined;
flow: Flow | null;
timeline: Timeline;
}) {
const layout = useMemo(() => layoutGraph(graph), [graph]);
const viewWidth = Math.max(layout.width, MIN_VIEW_WIDTH);
const viewHeight = Math.max(layout.height, MIN_VIEW_HEIGHT);
const step = state?.step;
const backward = step?.phase === "backward";
// The edges into the current node, or the edge from f when the backward pass starts
const isActive = (edge: PlacedEdge) =>
step !== undefined &&
(backward && step.partials.length === 0 ? edge.to === OUTPUT : edge.to === step.node && edge.to !== OUTPUT);
// Drawn last, so they sit on top
const edges = [...layout.edges.filter((edge) => !isActive(edge)), ...layout.edges.filter(isActive)];
return (
);
}