metrale_circuit/hardware/
render_matrix.rs

1// SPDX-License-Identifier: MIT OR Apache-2.0
2
3//! 2026-09-30: The roadmap matrix's pieces of a hardware report: its summary row and the
4//! per-device port lists (`kernels/circuits/plans/hw/MATRIX.md`).
5//!
6//! Owner: metrale-circuit (hardware).
7//! Invariants: deterministic text, formatted as [`super::render`] formats the reports.
8
9use std::collections::BTreeMap;
10use std::fmt::Write as _;
11
12use super::HwReport;
13use super::render::{FIT_CONTEXTS, FIT_SEQS, pct, projection};
14use crate::venn::Class;
15
16/// 2026-09-30: One device's port list: kernel to (why the device cannot run it, how many of the
17/// matrix's models need it).
18pub type PortList = BTreeMap<String, (String, usize)>;
19
20/// 2026-09-30: The port lists of a matrix: per device (in the order given), every rule kernel
21/// some model's policy could select that the device cannot run, with why and how many of the
22/// matrix's models are affected.
23pub fn port_lists(per_device: &[(String, PortList)]) -> String {
24    let mut s = String::from(
25        "## Port lists\n\nRule kernels a matrix model's policy could select that the device cannot run (the class does not compile them, its build compiles them out, or the device lacks the instruction), with the number of matrix models whose policy names them.\n",
26    );
27    for (device, kernels) in per_device {
28        let _ = writeln!(s, "\n### {device}\n");
29        if kernels.is_empty() {
30            let _ = writeln!(s, "None.");
31            continue;
32        }
33        let _ = writeln!(s, "| kernel | why | models |\n|---|---|---:|");
34        for (k, (why, n)) in kernels {
35            let _ = writeln!(s, "| {k} | {} | {n} |", why.replace('|', "\\|"));
36        }
37    }
38    s
39}
40
41/// 2026-09-30: The report's row of the roadmap matrix: coverage, the top five gaps at C=16,
42/// and the memory fit.
43pub fn summary_row(r: &HwReport) -> String {
44    let usable = r.resolved.device.memory_bytes * r.resolved.device.usable_fraction;
45    let fits = |c: u64| r.footprint.bytes(c, FIT_CONTEXTS[0]) <= usable;
46    let max_c = FIT_SEQS.iter().copied().filter(|&c| fits(c)).max();
47    let tok = |i: usize| {
48        r.tables.get(i).map_or_else(
49            || "-".into(),
50            |t| format!("{:.0}", t.planned.plan.rows as f64 / (t.total_us / 1e6)),
51        )
52    };
53    let wide = r.tables.get(1);
54    let mut gaps: Vec<(f64, String)> = wide
55        .map(|t| {
56            t.rows
57                .iter()
58                .filter(|x| !matches!(x.class, Class::Shared | Class::SharedUnmeasured))
59                .map(|x| {
60                    (
61                        x.share,
62                        format!("{} ({}, {})", x.site, x.class.name(), pct(x.share)),
63                    )
64                })
65                .collect()
66        })
67        .unwrap_or_default();
68    if let Some(t) = wide {
69        for f in &t.flags {
70            let kind = f.layer_kind.map_or("prologue/epilogue", |k| k.name());
71            gaps.push((f.share, format!("per-row {kind} ({})", pct(f.share))));
72        }
73    }
74    gaps.sort_by(|a, b| b.0.total_cmp(&a.0).then_with(|| a.1.cmp(&b.1)));
75    let top: Vec<String> = gaps.into_iter().take(5).map(|(_, g)| g).collect();
76    format!(
77        "| {} | {} | {} / {} | {} | {} / {} / {}{} | {} | {} | {} |",
78        r.model.label,
79        r.resolved.device.id,
80        pct(r.covered(0, true)),
81        pct(r.covered(1, true)),
82        pct(r.covered(1, false)),
83        tok(0),
84        tok(1),
85        tok(2),
86        projection(r),
87        if fits(1) {
88            "yes".to_string()
89        } else {
90            let need = r.footprint.bytes(1, FIT_CONTEXTS[0]);
91            format!("no: TP={}", (need / usable).ceil() as u64)
92        },
93        max_c.map_or_else(|| "none".into(), |c| c.to_string()),
94        if top.is_empty() {
95            "none".into()
96        } else {
97            top.join("; ")
98        }
99    )
100}