diff --git a/ts/routes/graphs/simulator.ts b/ts/routes/graphs/simulator.ts index e3fd94de0..e1c707029 100644 --- a/ts/routes/graphs/simulator.ts +++ b/ts/routes/graphs/simulator.ts @@ -11,10 +11,13 @@ import { min, pointer, rollup, + type ScaleLinear, scaleLinear, + type ScaleTime, scaleTime, schemeCategory10, select, + type Selection, } from "d3"; import * as tr from "@generated/ftl"; @@ -66,7 +69,7 @@ export function renderWorkloadChart( const subgraph_data = ({ [SimulateWorkloadSubgraph.ratio]: data.map(d => ({ ...d, - y: (60 * 60 * (d.memorized - d.start_memorized)) / d.timeCost, + y: (60 * 60 * (d.memorized - d.start_memorized)) / d.count, })), [SimulateWorkloadSubgraph.time]: data.map(d => ({ ...d, y: d.timeCost / d.learnSpan })), [SimulateWorkloadSubgraph.count]: data.map(d => ({ ...d, y: d.count / d.learnSpan })), @@ -100,6 +103,19 @@ export function renderWorkloadChart( return `${tr.deckConfigDesiredRetention()}: ${xTickFormat(dr)}
`; } + select(svgElem) + .enter() + .datum(subgraph_data[subgraph_data.length - 1]) + .append("line") + .attr("x1", bounds.marginLeft) + .attr("x2", bounds.width - bounds.marginRight) + .attr("y1", bounds.marginTop) + .attr("y2", bounds.marginTop) + .attr("stroke", "black") + .attr("stroke-width", 1); + + const startMemorized = subgraph_data[0].start_memorized; + return _renderSimulationChart( svgElem, bounds, @@ -110,6 +126,20 @@ export function renderWorkloadChart( (_e: MouseEvent, _d: number) => undefined, yTickFormat, xTickFormat, + (svg, x, y) => { + svg + .selectAll("line") + .data(subgraph == SimulateWorkloadSubgraph.memorized ? [startMemorized] : []) + .enter() + .attr("x1", x(xMin)) + .attr("x2", x(xMax)) + .attr("y1",d => y(d)) + .attr("y2",d => y(d)) + .attr("stroke", "black") + .attr("stroke-dasharray", "5,5") + .attr("stroke-width", 1); + }, + subgraph == SimulateWorkloadSubgraph.memorized ? startMemorized : 0, ); } @@ -190,16 +220,25 @@ export function renderSimulationChart( ); } -function _renderSimulationChart( +function _renderSimulationChart< + X extends ScaleLinear | ScaleTime, + T extends { x: any; y: any; label: number }, +>( svgElem: SVGElement, bounds: GraphBounds, subgraph_data: T[], - x: any, + x: X, formatY: (n: T["y"]) => string, formatX: (n: T["x"]) => string, legendMouseMove: (e: MouseEvent, d: number) => void, yTickFormat?: (n: number) => string, xTickFormat?: (n: number) => string, + renderExtra?: ( + svg: Selection, + x: X, + y: ScaleLinear, + ) => void, + y_min: number = Infinity, ): TableDatum[] { const svg = select(svgElem); svg.selectAll(".lines").remove(); @@ -220,7 +259,8 @@ function _renderSimulationChart( // y scale const yMax = max(subgraph_data, d => d.y)!; - const yMin = min(subgraph_data, d => d.y)!; + let yMin = min(subgraph_data, d => d.y)!; + yMin = min([yMin, y_min])!; const y = scaleLinear() .range([bounds.height - bounds.marginBottom, bounds.marginTop]) .domain([yMin, yMax]) @@ -248,7 +288,7 @@ function _renderSimulationChart( .attr("fill", "currentColor"); // x lines - const points = subgraph_data.map((d) => [x(d.x), y(d.y), d.label]); + const points = subgraph_data.map((d) => [x(d.x)!, y(d.y)!, d.label]); const groups = rollup(points, v => Object.assign(v, { z: v[0][2] }), d => d[2]); const color = schemeCategory10; @@ -370,6 +410,8 @@ function _renderSimulationChart( setDataAvailable(svg, true); + renderExtra?.(svg, x, y); + const tableData: TableDatum[] = []; return tableData;