==> src/infer.c <==1/* ui.rs's "5 infer" section, ported. */
2#include "mesh/top_ui_infer.h"
3
4#include "tui/chart.h"
5
6#include <math.h>
7
8typedef struct {
9 const char *name;
10 tui_color color;
11 tui_point *pts;
12 size_t n;
13} curve;
14
15/* The finite points of an f32 (x, y) series at two view offsets. */
16static size_t finite_pts(mt_frame *f, const mt_section *p, uint32_t xo, uint32_t yo, size_t count, tui_point **out) {
17 tui_point *pts = tui_arena_alloc(f->a, count * sizeof *pts);
18 size_t n = 0;
19 for (size_t i = 0; i < count; i++) {
20 float x = mt_section_view_f32(p, xo + 4 * i), y = mt_section_view_f32(p, yo + 4 * i);
21 if (isfinite(x) && isfinite(y)) pts[n++] = (tui_point){(double)x, (double)y};
22 }
23 *out = pts;
24 return n;
25}
26
27void mt_draw_infer(mt_frame *f, tui_rect area, const mt_app *app) {
28 const mt_machine *m = mt_app_focused(app);
29 tui_color color;
30 tui_line title = mt_machine_title(f, m, true, "infer: model vs sin(pi x)", &color);
31 tui_block block = mt_panel(f, title, color);
32 tui_rect inner = tui_block_inner(&block, area);
33 mt_block(f, &block, area);
34 const mt_section *inf = mt_bound(f, inner, m, "infer");
35 if (!inf) return;
36 tui_rect hs[2];
37 tui_hsplit(inner, 2, (tui_constraint[]){TUI_FILL(1), TUI_LENGTH(30)}, hs);
38 /* The target samples live in train's view (infer reads train's params the same way). */
39 const mt_section *train = mt_machine_program(m, "train");
40 tui_point *target = NULL;
41 size_t ntarget = train ? finite_pts(f, train, MESH_TRAIN_T_X, MESH_TRAIN_T_Y, 256, &target) : 0;
42 curve *curves = tui_arena_alloc(f->a, (app->nmachines + 1) * sizeof *curves);
43 size_t nc = 0;
44 curves[nc].name = mt_name(f, m);
45 curves[nc].color = MT_MACHINE[app->focus % 8];
46 curves[nc].n = finite_pts(f, inf, MESH_INFER_I_X, MESH_INFER_I_Y, MESH_INFER_POINTS, &curves[nc].pts);
47 nc++;
48 for (size_t i = 0; i < app->nmachines; i++) {
49 const mt_section *op;
50 if (i == app->focus || !(op = mt_machine_program(&app->machines[i], "infer"))) continue;
51 curves[nc].name = mt_name(f, &app->machines[i]);
52 curves[nc].color = MT_MACHINE[i % 8];
53 curves[nc].n = finite_pts(f, op, MESH_INFER_I_X, MESH_INFER_I_Y, MESH_INFER_POINTS, &curves[nc].pts);
54 nc++;
55 }
56 double x0 = INFINITY, x1 = -INFINITY, y0 = INFINITY, y1 = -INFINITY;
57 for (size_t i = 0; i < ntarget; i++) {
58 x0 = fmin(x0, target[i].x);
59 x1 = fmax(x1, target[i].x);
60 y0 = fmin(y0, target[i].y);
61 y1 = fmax(y1, target[i].y);
62 }
63 for (size_t c = 0; c < nc; c++)
64 for (size_t i = 0; i < curves[c].n; i++) {
65 x0 = fmin(x0, curves[c].pts[i].x);
66 x1 = fmax(x1, curves[c].pts[i].x);
67 y0 = fmin(y0, curves[c].pts[i].y);
68 y1 = fmax(y1, curves[c].pts[i].y);
69 }
70 if (!isfinite(x0) || x1 <= x0) {
71 x0 = -1.0;
72 x1 = 1.0;
73 }
74 if (!isfinite(y0) || y1 <= y0) {
75 y0 = -1.0;
76 y1 = 1.0;
77 }
78 double pad = (y1 - y0) * 0.08;
79 y0 -= pad;
80 y1 += pad;
81 tui_dataset *sets = tui_arena_alloc(f->a, (nc + 1) * sizeof *sets);
82 sets[0] = tui_dataset_new();
83 sets[0].has_name = true;
84 sets[0].name = tui_line_from_str(f->a, "target sin(pi x)");
85 sets[0].marker = TUI_MARKER_DOT;
86 sets[0].graph_type = TUI_GRAPH_SCATTER;
87 sets[0].style = tui_fg(MT_DIM);
88 sets[0].data = target;
89 sets[0].n = ntarget;
90 /* Draw the other machines first so the focused curve stays on top. */
91 for (size_t c = 0; c < nc; c++) {
92 const curve *cv = &curves[nc - 1 - c];
93 tui_dataset *d = &sets[1 + c];
94 *d = tui_dataset_new();
95 d->has_name = true;
96 d->name = tui_line_from_str(f->a, cv->name);
97 d->marker = TUI_MARKER_BRAILLE;
98 d->graph_type = TUI_GRAPH_LINE;
99 d->style = tui_fg(cv->color);
100 d->data = cv->pts;
101 d->n = cv->n;
102 }
103 tui_line *xl = tui_arena_alloc(f->a, 2 * sizeof *xl), *yl = tui_arena_alloc(f->a, 2 * sizeof *yl);
104 xl[0] = tui_line_from_str(f->a, mt_ff(f, x0, 2));
105 xl[1] = tui_line_from_str(f->a, MT_FMT(f, "x %s", mt_ff(f, x1, 2)));
106 yl[0] = tui_line_from_str(f->a, mt_ff(f, y0, 2));
107 yl[1] = tui_line_from_str(f->a, mt_ff(f, y1, 2));
108 tui_chart chart = tui_chart_new(sets, nc + 1);
109 chart.x_axis = tui_axis_new();
110 chart.x_axis.style = tui_fg(MT_DIM);
111 chart.x_axis.bounds[0] = x0;
112 chart.x_axis.bounds[1] = x1;
113 chart.x_axis.labels = xl;
114 chart.x_axis.nlabels = 2;
115 chart.y_axis = tui_axis_new();
116 chart.y_axis.style = tui_fg(MT_DIM);
117 chart.y_axis.bounds[0] = y0;
118 chart.y_axis.bounds[1] = y1;
119 chart.y_axis.labels = yl;
120 chart.y_axis.nlabels = 2;
121 chart.has_legend = true;
122 chart.legend_position = TUI_LEGEND_BOTTOM_RIGHT;
123 chart.style = tui_style_bg(tui_style_new(), MT_BG);
124 tui_chart_render(f->a, &chart, hs[0], f->buf);
125
126 mt_lines lines = {0};
127 mt_lines_push(f, &lines, MT_LINE(f, mt_label("infer "), mt_styled(mt_section_state_name(inf), mt_state_style(inf))));
128 mt_lines_push(f, &lines, MT_LINE(f, mt_label("points "), mt_val(MT_FMT(f, "%u", MESH_INFER_POINTS))));
129 mt_lines_push(f, &lines, tui_line_span(f->a, mt_label("y = a + bx + cx² + dx³")));
130 /* The params infer's last run read from train's view, and train's step then. */
131 static const char *const pn[4] = {"a", "b", "c", "d"};
132 for (size_t k = 0; k < 4; k++)
133 mt_lines_push(f, &lines,
134 MT_LINE(f, mt_label(MT_FMT(f, "%s ", pn[k])), mt_val(mt_ffp(f, mt_section_view_f32(inf, MESH_INFER_I_PARAMS + 4 * k), 6))));
135 mt_lines_push(f, &lines, MT_LINE(f, mt_label("runs "), mt_val(MT_FMT(f, "%u", mt_section_view_u32(inf, MESH_INFER_I_RUNS)))));
136 mt_lines_push(f, &lines, MT_LINE(f, mt_label("train step "), mt_val(MT_FMT(f, "%u", mt_section_view_u32(inf, MESH_INFER_I_TRAIN_STEP)))));
137 if (!train) mt_lines_push(f, &lines, tui_line_span(f->a, mt_fgs("train is not loaded", MT_WARN)));
138 tui_block b = tui_block_new();
139 b.borders = TUI_BORDER_LEFT;
140 b.border_style = tui_fg(MT_FAINT);
141 tui_paragraph p = tui_paragraph_new(mt_text(f, &lines));
142 p.block = &b;
143 tui_paragraph_render(f->a, &p, hs[1], f->buf);
144}