Add 3x9 factorial analysis of std79-doe heartbeat/physics telemetry
Build / build-amd64-iso (push) Canceled after 0s
Build / build-aarch64-iso (push) Canceled after 0s
Build / build-riscv64-img (push) Canceled after 0s

Correlates every HB-ON/HB-OFF heartbeat-tick CSV row (results-20260912-
with-heartbeat-csv/*-doe-raw.log) back to which trial (run_id, id_idx,
id_label, rep) was active when it printed, despite the async tick
printer splicing rows mid-token -- including mid a DOE-RUN marker itself
-- into the trial loop's own console output on the shared serial line.

Pipeline (analysis-20260912/, see its own README.md):
- correlate_doe.py: two-pass reconstruction per architecture (remove
  atomic CSV-row spans to rebuild the clean trial-output stream, map
  each removed row's offset back to the nearest preceding run_id
  marker); identity/rep looked up from a known-clean prior run's
  run_id mapping rather than re-parsed, since one aarch64 marker
  (trial 12, rajames rep 1) lost its id_idx digit to a zero-separator
  collision with an adjacent CSV field and is unrecoverable from that
  log alone -- its rows fold into trial 11 instead, documented as a
  known limitation.
- combine.py: merges all three architectures into combined.csv (768
  rows), decoding Q48.16 fields to floats and jitter_bits' IEEE754 bit
  pattern to real jitter_ns.
- analysis.R: per-cell (architecture x identity) means/SD and two-way
  ANOVA for each of 12 telemetry metrics, one boxplot SVG per metric,
  written up as ANALYSIS.md.

Key findings: identity significantly affects word-heat/window-sizing
metrics (expected -- different identities execute different word
sets), architecture significantly affects timing metrics (APIC
ticks/tick, timing variance, fleet heat -- expected, different QEMU
targets), zero architecture x identity interaction on any metric.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01EXieurDfDSsDFdnSyusuWo
This commit is contained in:
Robert Allan James
2026-09-12 12:46:34 -04:00
co-authored by Claude Sonnet 5
parent 403a7639e1
commit 934be5a257
19 changed files with 5872 additions and 0 deletions
+12
View File
@@ -68,3 +68,15 @@ nothing is corrupted in memory — only the printed character stream interleaves
tooling that wants an exact reconstruction of the trial-output stream from these three raw logs
needs to account for that (strip `[HADES][DOE ] ...` fragments and rejoin) rather than assume
one physical line is one logical print.
`analysis-20260912/` builds exactly that reconstruction and turns it into a real 3×9 factorial
analysis: `correlate_doe.py` labels every heartbeat-tick row with the trial that was active when
it printed, `combine.py` merges all three architectures into `combined.csv` (768 rows, Q48.16
fields decoded to floats), and `analysis.R` computes per-cell (architecture × identity) means/SD
and a two-way ANOVA for each of 12 telemetry metrics, rendering one boxplot SVG per metric. See
`analysis-20260912/README.md` for the pipeline and `analysis-20260912/ANALYSIS.md` for the
report itself — key findings: identity significantly affects word-heat/window-sizing metrics
(expected — different identities execute different word sets), architecture significantly
affects timing metrics (APIC ticks/tick, timing variance, fleet heat — expected, different QEMU
targets), and **zero architecture × identity interaction on any metric** — the two factors'
effects are clean and separable, not confounded.
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,55 @@
# std79-doe 3×9 factorial analysis (heartbeat/physics telemetry)
Read `ANALYSIS.md` for the report itself (key findings, per-metric cell means,
two-way ANOVA tables, boxplot SVGs). This file just documents the pipeline.
## Pipeline
1. **Source data:** `../results-20260912-with-heartbeat-csv/{amd64,aarch64,riscv64}-doe-raw.log`
— raw QEMU serial logs from the `HB-ON`/`HB-OFF`-instrumented std79 DoE campaign
(see `../README.md` and FABRIC-3.md §XV/§XVI/§XVII for the campaign itself).
2. **`correlate_doe.py <raw_log> <out_csv>`** — reconstructs which DoE trial
(`run_id`, `id_idx`, `id_label`, `rep`) was active for every heartbeat-tick CSV
row, despite the async tick printer splicing rows mid-token into the trial
loop's own console output on the shared serial line (see the script's own
docstring for the two-pass reconstruction method, and its `RUN_ID_MAP` for
why identity/rep are looked up from a known-clean prior run rather than
re-parsed from each log — one aarch64 marker was unrecoverable, see below).
Run once per architecture:
```
python3 correlate_doe.py ../results-20260912-with-heartbeat-csv/amd64-doe-raw.log amd64-correlated.csv
python3 correlate_doe.py ../results-20260912-with-heartbeat-csv/aarch64-doe-raw.log aarch64-correlated.csv
python3 correlate_doe.py ../results-20260912-with-heartbeat-csv/riscv64-doe-raw.log riscv64-correlated.csv
```
3. **`combine.py <in1.csv> [in2.csv ...] <out.csv>`** — merges the three
per-architecture correlated CSVs into `combined.csv` (committed, 768 rows),
decoding Q48.16 fixed-point fields to plain floats and `jitter_bits`' raw
IEEE754 bit pattern to a real `jitter_ns` value:
```
python3 combine.py amd64-correlated.csv aarch64-correlated.csv riscv64-correlated.csv combined.csv
```
4. **`analysis.R`** (run from this directory) — reads `combined.csv`, computes
per-cell (architecture × identity) summary statistics and a two-way ANOVA
for each of the 12 varying telemetry metrics, renders one boxplot SVG per
metric to `svg/`, and writes `ANALYSIS.md`:
```
Rscript analysis.R
```
Requires `dplyr`, `tidyr`, `ggplot2`, `svglite` (all present in this
environment already).
## Known limitation
aarch64's raw log lost 1 of 27 `DOE-RUN` markers to interleaving beyond
recovery — trial 12 (identity `rajames`, rep 1). Its rows fold into trial 11
(identity `rajames`, rep 0) in `combined.csv`, so aarch64's `rajames` cell
isn't perfectly separable between those two replicates specifically. Every
other cell, on every architecture, is unaffected — see `ANALYSIS.md`'s own
note and `correlate_doe.py`'s docstring for the full explanation.
@@ -0,0 +1,243 @@
#!/usr/bin/env Rscript
# analysis.R -- 3(architecture) x 9(identity) factorial analysis of the
# std79-doe heartbeat/physics telemetry captured via HB-ON/HB-OFF
# (see ../README.md's results-20260912-with-heartbeat-csv/ entry).
#
# Input: combined.csv (this directory) -- one row per heartbeat tick,
# tagged with which (arch, run_id, id_idx, id_label, rep) trial was
# active when that tick's CSV row was printed, built by
# correlate_doe.py from the three raw logs in
# ../results-20260912-with-heartbeat-csv/. Q48.16 fields are already
# decoded to plain floats (avg_word_heat, time_trust, variance,
# hera/hermes/artemis_heat) and jitter_bits to jitter_ns (IEEE754
# bit-pattern reinterpretation), both done in Python before this script
# runs, since R has no native uint64/bit-punning story worth using here.
#
# Output: ANALYSIS.md (this directory) + svg/*.svg (referenced from it).
suppressMessages({
library(dplyr)
library(tidyr)
library(ggplot2)
library(svglite)
})
set.seed(1)
# Run from this directory (contains combined.csv); the caller cd's here.
df <- read.csv("combined.csv", stringsAsFactors = FALSE)
id_levels <- c("zuse", "rajames", "00", "01", "02", "03", "04", "05", "06")
arch_levels <- c("amd64", "aarch64", "riscv64")
df$id_label <- factor(df$id_label, levels = id_levels)
df$arch <- factor(df$arch, levels = arch_levels)
# apic_ticks is a monotonic hardware counter, not directly comparable
# across trials/architectures as a raw value -- the per-tick DELTA
# (hardware ticks consumed per heartbeat tick) is the actual timing
# signal. Computed per (arch, run_id) so a trial boundary never
# contaminates the delta of the row after it.
df <- df %>%
arrange(arch, run_id, tick_number) %>%
group_by(arch, run_id) %>%
mutate(apic_delta = apic_ticks - lag(apic_ticks)) %>%
ungroup()
metrics <- c(
"hot_word_count", "avg_word_heat", "window_width", "actual_window_size",
"jitter_ns", "apic_delta", "time_trust", "variance", "vm_call_depth_max",
"hera_heat", "hermes_heat", "artemis_heat"
)
metric_labels <- c(
hot_word_count = "Hot word count",
avg_word_heat = "Mean word execution heat",
window_width = "Rolling window width",
actual_window_size = "Actual analysis window size",
jitter_ns = "Estimated timer jitter (ns)",
apic_delta = "APIC ticks per heartbeat tick",
time_trust = "TIME-TRUST",
variance = "Timing variance",
vm_call_depth_max = "Max VM call depth",
hera_heat = "Hera fleet heat",
hermes_heat = "Hermes fleet heat",
artemis_heat = "Artemis fleet heat"
)
dir.create("svg", showWarnings = FALSE)
# ---- Per-cell (arch x id_label) summary table for one metric ----
cell_summary <- function(metric) {
df %>%
filter(!is.na(.data[[metric]])) %>%
group_by(arch, id_label) %>%
summarise(
n = n(),
mean = mean(.data[[metric]]),
sd = sd(.data[[metric]]),
cv_pct = ifelse(mean != 0, 100 * sd / abs(mean), NA_real_),
.groups = "drop"
)
}
# ---- Two-way ANOVA (metric ~ arch * id_label), safe on zero-variance ----
anova_table <- function(metric) {
d <- df %>% filter(!is.na(.data[[metric]]))
if (length(unique(d[[metric]])) <= 1) {
return(NULL)
}
fit <- tryCatch(
aov(as.formula(paste0(metric, " ~ arch * id_label")), data = d),
error = function(e) NULL
)
if (is.null(fit)) return(NULL)
s <- summary(fit)[[1]]
s
}
# ---- Boxplot: metric by id_label, faceted by arch ----
make_plot <- function(metric) {
d <- df %>% filter(!is.na(.data[[metric]]))
if (length(unique(d[[metric]])) <= 1) return(NULL)
p <- ggplot(d, aes(x = id_label, y = .data[[metric]], fill = id_label)) +
geom_boxplot(outlier.size = 0.6, alpha = 0.85) +
facet_wrap(~arch, ncol = 1) +
labs(
title = metric_labels[[metric]],
x = "Identity", y = metric_labels[[metric]]
) +
theme_bw(base_size = 11) +
theme(legend.position = "none", axis.text.x = element_text(angle = 45, hjust = 1))
fname <- file.path("svg", paste0(metric, ".svg"))
ggsave(fname, p, width = 7, height = 8, device = svglite)
fname
}
# ---- Run everything, collect markdown fragments ----
md <- c()
md <- c(md, "# std79-doe 3×9 Factorial Analysis — Heartbeat/Physics Telemetry")
md <- c(md, "")
md <- c(md, sprintf("Generated %s. Source: `combined.csv` (%d rows), built by", format(Sys.time(), "%Y-%m-%d %H:%M"), nrow(df)))
md <- c(md, "`correlate_doe.py` from the three raw logs in")
md <- c(md, "`../results-20260912-with-heartbeat-csv/`. See that file's own docstring for the")
md <- c(md, "correlation methodology (the heartbeat tick's async CSV printer and the DoE trial")
md <- c(md, "loop's own console output share one serial line with no locking, so rows can land")
md <- c(md, "mid-token in the raw logs; the underlying FORTH execution and values are")
md <- c(md, "unaffected, only reconstructing which trial owns which row needed care).")
md <- c(md, "")
md <- c(md, "**Design:** 3 architectures (amd64, aarch64, riscv64) × 9 identities (zuse,")
md <- c(md, "rajames, 00-06) × 3 replicates, Fisher-Yates-shuffled run order, same seed")
md <- c(md, "(12345) on every architecture (FABRIC-3.md §XV). Each of the 27 trials per")
md <- c(md, "architecture runs the same 24-word FORTH-79 exerciser; every heartbeat tick during")
md <- c(md, "the whole `EXEC-STD79-DOE` run (bracketed by `HB-ON`/`HB-OFF`) emits one telemetry")
md <- c(md, "row (`doe_log.c`), correlated back to whichever trial was active when it printed.")
md <- c(md, "")
md <- c(md, "**Known limitation:** aarch64's raw log lost 1 of 27 run_id markers to interleaving")
md <- c(md, "beyond recovery (trial 12, identity `rajames` rep 1) -- its rows are folded into")
md <- c(md, "trial 11 (identity `rajames` rep 0) in this dataset, so aarch64's `rajames` cell")
md <- c(md, "for rep-sensitive metrics is not perfectly separable for those two replicates")
md <- c(md, "specifically. Every other cell on every architecture is unaffected.")
md <- c(md, "")
# ---- Pass 1: compute everything, so the "Key findings" summary (which
# needs every metric's ANOVA result) can be written before the detailed
# per-metric sections that follow it. ----
results <- list()
for (metric in metrics) {
results[[metric]] <- list(
cs = cell_summary(metric),
at = anova_table(metric),
plot_file = make_plot(metric)
)
}
sig <- function(p) !is.na(p) && p < 0.05
arch_sig <- c(); id_sig <- c(); inter_sig <- c(); none_sig <- c()
for (metric in metrics) {
at <- results[[metric]]$at
if (is.null(at)) next
p_arch <- if ("arch" %in% trimws(rownames(at))) at[trimws(rownames(at)) == "arch", "Pr(>F)"] else NA
p_id <- if ("id_label" %in% trimws(rownames(at))) at[trimws(rownames(at)) == "id_label", "Pr(>F)"] else NA
p_int <- if ("arch:id_label" %in% trimws(rownames(at))) at[trimws(rownames(at)) == "arch:id_label", "Pr(>F)"] else NA
label <- metric_labels[[metric]]
any_sig <- FALSE
if (sig(p_arch)) { arch_sig <- c(arch_sig, label); any_sig <- TRUE }
if (sig(p_id)) { id_sig <- c(id_sig, label); any_sig <- TRUE }
if (sig(p_int)) { inter_sig <- c(inter_sig, label); any_sig <- TRUE }
if (!any_sig) none_sig <- c(none_sig, label)
}
md <- c(md, "## Key findings (p < 0.05, two-way ANOVA)")
md <- c(md, "")
md <- c(md, sprintf("- **Architecture main effect:** %s",
if (length(arch_sig)) paste(arch_sig, collapse = ", ") else "none"))
md <- c(md, sprintf("- **Identity main effect:** %s",
if (length(id_sig)) paste(id_sig, collapse = ", ") else "none"))
md <- c(md, sprintf("- **Architecture × identity interaction:** %s",
if (length(inter_sig)) paste(inter_sig, collapse = ", ") else "none"))
md <- c(md, sprintf("- **No significant effect of either factor:** %s",
if (length(none_sig)) paste(none_sig, collapse = ", ") else "none"))
md <- c(md, "")
md <- c(md, "TIME-TRUST's architecture effect is a numerical artifact worth reading correctly,")
md <- c(md, "not a substantive result: it is essentially constant *within* each architecture")
md <- c(md, "(residual variance ~1e-31, i.e. floating-point noise) and differs *between*")
md <- c(md, "architectures — so the F statistic is enormous simply because the within-group")
md <- c(md, "denominator is near zero, not because TIME-TRUST is meaningfully more variable")
md <- c(md, "across architectures than the other metrics here. It is architecture-determined")
md <- c(md, "and identity-independent, which is itself the interesting part.")
md <- c(md, "")
for (metric in metrics) {
cs <- results[[metric]]$cs
at <- results[[metric]]$at
plot_file <- results[[metric]]$plot_file
md <- c(md, sprintf("## %s (`%s`)", metric_labels[[metric]], metric))
md <- c(md, "")
if (is.null(plot_file)) {
md <- c(md, "_Constant across every trial and architecture — no variance, excluded from ANOVA/plot._")
md <- c(md, "")
next
}
md <- c(md, sprintf("![%s](%s)", metric_labels[[metric]], plot_file))
md <- c(md, "")
md <- c(md, "| Architecture | Identity | n | mean | sd | CV% |")
md <- c(md, "|---|---|---:|---:|---:|---:|")
for (i in seq_len(nrow(cs))) {
r <- cs[i, ]
md <- c(md, sprintf("| %s | %s | %d | %.4g | %.4g | %s |",
r$arch, r$id_label, r$n, r$mean, r$sd,
ifelse(is.na(r$cv_pct), "—", sprintf("%.2f", r$cv_pct))))
}
md <- c(md, "")
if (!is.null(at)) {
md <- c(md, "Two-way ANOVA (`metric ~ arch * id_label`):")
md <- c(md, "")
md <- c(md, "| Term | Df | Sum Sq | Mean Sq | F value | Pr(>F) |")
md <- c(md, "|---|---:|---:|---:|---:|---:|")
terms <- rownames(at)
for (i in seq_len(nrow(at))) {
row <- at[i, ]
# %g-style: near-zero within-cell residual variance (e.g. a metric
# that's essentially constant within an architecture, only varying
# BETWEEN architectures -- TIME-TRUST does exactly this) can blow
# the F statistic up to absurd magnitudes; format generally rather
# than with a fixed decimal count so that renders sanely too.
fval <- if (!is.na(row[["F value"]])) formatC(row[["F value"]], format = "g", digits = 4) else "—"
pval <- if (!is.na(row[["Pr(>F)"]])) formatC(row[["Pr(>F)"]], format = "g", digits = 4) else "—"
md <- c(md, sprintf("| %s | %d | %.4g | %.4g | %s | %s |",
trimws(terms[i]), row[["Df"]], row[["Sum Sq"]], row[["Mean Sq"]], fval, pval))
}
md <- c(md, "")
} else {
md <- c(md, "_ANOVA not computed (insufficient variance)._")
md <- c(md, "")
}
}
writeLines(md, "ANALYSIS.md")
cat("Wrote ANALYSIS.md and svg/*.svg\n")
@@ -0,0 +1,60 @@
#!/usr/bin/env python3
"""
combine.py -- build combined.csv from the three per-architecture
correlated CSVs (correlate_doe.py's output), decoding Q48.16 fixed-point
fields to plain floats and jitter_bits' IEEE754 bit pattern to a real
jitter_ns value, since R has no convenient native uint64/bit-punning
story worth using here.
Usage (from this directory, after running correlate_doe.py for each
architecture against ../results-20260912-with-heartbeat-csv/*-doe-raw.log
to produce amd64-correlated.csv / aarch64-correlated.csv /
riscv64-correlated.csv):
python3 combine.py amd64-correlated.csv aarch64-correlated.csv riscv64-correlated.csv combined.csv
"""
import csv
import struct
import sys
Q48 = 65536.0
def main():
if len(sys.argv) < 3:
print("usage: combine.py <in1.csv> [in2.csv ...] <out.csv>", file=sys.stderr)
sys.exit(1)
*in_paths, out_path = sys.argv[1:]
fields = ["arch", "run_id", "id_idx", "id_label", "rep", "tick_number", "elapsed_ns",
"tick_interval_ns", "hot_word_count", "avg_word_heat", "window_width",
"actual_window_size", "predicted_label_hits", "jitter_ns", "apic_ticks",
"time_trust", "variance", "vm_call_depth_max", "hera_heat", "hermes_heat",
"artemis_heat"]
out_rows = []
for path in in_paths:
arch = path.split("-correlated.csv")[0].split("/")[-1]
with open(path) as f:
for row in csv.DictReader(f):
jitter_bits = int(row["jitter_bits"])
jitter_ns = struct.unpack('<d', struct.pack('<Q', jitter_bits))[0]
out = dict(row)
out["arch"] = arch
out["avg_word_heat"] = int(row["avg_word_heat_q48"]) / Q48
out["time_trust"] = int(row["time_trust_q48"]) / Q48
out["variance"] = int(row["variance_q48"]) / Q48
out["hera_heat"] = int(row["hera_heat_q48"]) / Q48
out["hermes_heat"] = int(row["hermes_heat_q48"]) / Q48
out["artemis_heat"] = int(row["artemis_heat_q48"]) / Q48
out["jitter_ns"] = jitter_ns
out_rows.append({k: out[k] for k in fields})
with open(out_path, "w", newline="") as f:
w = csv.DictWriter(f, fieldnames=fields)
w.writeheader()
w.writerows(out_rows)
print(f"rows written: {len(out_rows)}")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,137 @@
#!/usr/bin/env python3
"""
correlate_doe.py <raw_log> <out_csv>
Reconstructs which DoE trial (run_id, id_idx, id_label, rep) was active
for every heartbeat-tick CSV row in a std79-doe raw log, despite the
async HB-ON tick printer splicing CSV rows mid-token into the trial
loop's own console output on the shared serial line -- including mid a
DOE-RUN,... marker itself.
Method:
1. Strip ANSI codes and the per-line "[VM-TAG] " console prefix (an
artifact of the log capture layer), concatenate every stripped
line with no separator. Find every [HADES][DOE ] header/row
occurrence and physically remove exactly those spans, recording
each removed data row's insertion offset in the CLEANED stream's
own coordinates.
2. Parse only "DOE-RUN,<run_id> ," (the run_id digits) from the clean
stream -- reliable even when a CSV row spliced into the id_idx/
id_label/rep text right after it (observed live: a row's last
digit field can land with zero separator against the immediately
following trial digit, e.g. "...11642" + "6" -> "116426", making
id_idx unrecoverable from THIS log alone for the affected trial).
id_idx/id_label/rep are looked up from RUN_ID_MAP below instead of
re-parsed -- deterministic given the campaign's fixed seed (12345),
independently verified identical across 5+ prior clean runs
(results-20260911-donor-floor-fix/amd64-doe-raw.log and others).
3. Assign each CSV row to whichever run_id marker's start offset is
the largest one at or before that row's insertion offset.
"""
import re
import sys
import csv
import bisect
# From results-20260911-donor-floor-fix/amd64-doe-raw.log (seed 12345),
# independently re-verified identical (md5) across every std79-doe run
# since e2abc56. run_id -> (id_idx, id_label, rep).
RUN_ID_MAP = {
0: (6, "04", 2), 1: (3, "01", 1), 2: (0, "zuse", 1), 3: (1, "rajames", 0),
4: (5, "03", 0), 5: (0, "zuse", 2), 6: (2, "00", 2), 7: (8, "06", 2),
8: (0, "zuse", 0), 9: (6, "04", 1), 10: (6, "04", 0), 11: (4, "02", 2),
12: (1, "rajames", 1), 13: (7, "05", 1), 14: (7, "05", 2), 15: (4, "02", 0),
16: (2, "00", 0), 17: (8, "06", 0), 18: (7, "05", 0), 19: (2, "00", 1),
20: (8, "06", 1), 21: (5, "03", 2), 22: (1, "rajames", 2), 23: (3, "01", 2),
24: (5, "03", 1), 25: (3, "01", 0), 26: (4, "02", 1),
}
def build_stream(raw_text):
parts = []
for line in raw_text.split('\n'):
m = re.match(r'^\[[^\]]+\]\s?(.*)$', line)
parts.append(m.group(1) if m else line)
return ''.join(parts)
ROW_RE = re.compile(
r'\[HADES\]\[DOE \] '
r'(?:(?P<header>tick_number,elapsed_ns,[^\[]*?)(?=\[HADES\]\[DOE \]|DOE-RUN|$)'
r'|(?P<row>[0-9]+(?:,[0-9]+)+))'
)
RUN_ID_RE = re.compile(r'DOE-RUN,\s*([0-9]+)\s*,')
def main():
if len(sys.argv) != 3:
print("usage: correlate_doe.py <raw_log> <out_csv>", file=sys.stderr)
sys.exit(1)
log_path, out_path = sys.argv[1], sys.argv[2]
with open(log_path, "r", errors="replace") as f:
raw = f.read()
raw = re.sub(r'\x1b\[[0-9;]*[a-zA-Z]', '', raw)
stream = build_stream(raw)
cleaned_parts = []
cleaned_offset = 0
last_end = 0
csv_rows = [] # (cleaned_insertion_offset, fields)
header_fields = None
for m in ROW_RE.finditer(stream):
before = stream[last_end:m.start()]
cleaned_parts.append(before)
cleaned_offset += len(before)
if m.group('header') is not None:
header_fields = [x.strip() for x in m.group('header').split(',')]
else:
csv_rows.append((cleaned_offset, m.group('row').split(',')))
last_end = m.end()
cleaned_parts.append(stream[last_end:])
cleaned_stream = ''.join(cleaned_parts)
if header_fields is None:
print("FATAL: never found the DOE CSV header row", file=sys.stderr)
sys.exit(1)
markers = [(m.start(), int(m.group(1))) for m in RUN_ID_RE.finditer(cleaned_stream)]
# Sanity: run_ids should appear in increasing offset order with values
# 0..26 each exactly once. Warn, don't crash, on anything unexpected --
# a genuinely dropped/garbled run_id digit is possible in principle.
seen = [rid for _, rid in markers]
if sorted(set(seen)) != list(range(27)) or len(seen) != 27:
print(f"WARNING: expected exactly run_ids 0..26 once each, got {sorted(seen)}", file=sys.stderr)
marker_offsets = [off for off, _ in markers]
out_rows = []
unlabeled = 0
for offset, fields in csv_rows:
i = bisect.bisect_right(marker_offsets, offset) - 1
if i >= 0:
run_id = markers[i][1]
id_idx, id_label, rep = RUN_ID_MAP.get(run_id, (None, None, None))
elif 0 not in seen:
# No marker precedes this row because run_id 0's own marker
# (always the very first trial, loop index 0) got garbled
# beyond recovery in this particular log -- safe to assume
# every row before the first RECOVERED marker still belongs
# to trial 0, not truly unlabeled.
run_id = 0
id_idx, id_label, rep = RUN_ID_MAP[0]
else:
run_id, id_idx, id_label, rep = None, None, None, None
unlabeled += 1
out_rows.append(((run_id, id_idx, id_label, rep), fields))
with open(out_path, "w", newline="") as f:
w = csv.writer(f)
w.writerow(["run_id", "id_idx", "id_label", "rep"] + header_fields)
for (run_id, id_idx, id_label, rep), fields in out_rows:
w.writerow([run_id, id_idx, id_label, rep] + fields)
print(f"trial markers found: {len(markers)}")
print(f"data rows: {len(out_rows)}, unlabeled (before first marker): {unlabeled}")
labeled_run_ids = sorted(set(t[0] for (t, _) in out_rows if t[0] is not None))
print(f"distinct run_ids covered: {len(labeled_run_ids)} -> {labeled_run_ids}")
if __name__ == "__main__":
main()
File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 35 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 43 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 56 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 34 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 42 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 41 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 34 KiB

@@ -0,0 +1,299 @@
<?xml version='1.0' encoding='UTF-8' ?>
<svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' width='504.00pt' height='576.00pt' viewBox='0 0 504.00 576.00'>
<g class='svglite'>
<defs>
<style type='text/css'><![CDATA[
.svglite line, .svglite polyline, .svglite polygon, .svglite path, .svglite rect, .svglite circle {
fill: none;
stroke: #000000;
stroke-linecap: round;
stroke-linejoin: round;
stroke-miterlimit: 10.00;
}
.svglite text {
white-space: pre;
}
.svglite g.glyphgroup path {
fill: inherit;
stroke: none;
}
]]></style>
</defs>
<rect width='100%' height='100%' style='stroke: none; fill: #FFFFFF;'/>
<defs>
<clipPath id='cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA='>
<rect x='0.00' y='0.00' width='504.00' height='576.00' />
</clipPath>
</defs>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
<rect x='0.00' y='0.00' width='504.00' height='576.00' style='stroke-width: 1.07; stroke: #FFFFFF; fill: #FFFFFF;' />
</g>
<defs>
<clipPath id='cpMzUuMjJ8NDk4LjUyfDM5LjQyfDE4Ni42Mw=='>
<rect x='35.22' y='39.42' width='463.30' height='147.21' />
</clipPath>
</defs>
<g clip-path='url(#cpMzUuMjJ8NDk4LjUyfDM5LjQyfDE4Ni42Mw==)'>
<rect x='35.22' y='39.42' width='463.30' height='147.21' style='stroke-width: 1.07; stroke: none; fill: #FFFFFF;' />
<polyline points='35.22,158.72 498.52,158.72 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,116.28 498.52,116.28 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,73.84 498.52,73.84 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,179.94 498.52,179.94 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,137.50 498.52,137.50 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,95.06 498.52,95.06 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,52.62 498.52,52.62 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='65.43,186.63 65.43,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='115.79,186.63 115.79,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='166.15,186.63 166.15,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='216.51,186.63 216.51,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='266.87,186.63 266.87,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='317.23,186.63 317.23,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='367.59,186.63 367.59,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='417.95,186.63 417.95,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='468.31,186.63 468.31,39.42 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<line x1='65.43' y1='46.11' x2='65.43' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='65.43' y1='46.11' x2='65.43' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='46.55,46.11 46.55,46.11 84.32,46.11 84.32,46.11 46.55,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #F8766D; fill-opacity: 0.85;' />
<line x1='46.55' y1='46.11' x2='84.32' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='115.79' y1='46.11' x2='115.79' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='115.79' y1='46.11' x2='115.79' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='96.91,46.11 96.91,46.11 134.68,46.11 134.68,46.11 96.91,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #D39200; fill-opacity: 0.85;' />
<line x1='96.91' y1='46.11' x2='134.68' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='166.15' y1='46.11' x2='166.15' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='166.15' y1='46.11' x2='166.15' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='147.27,46.11 147.27,46.11 185.04,46.11 185.04,46.11 147.27,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #93AA00; fill-opacity: 0.85;' />
<line x1='147.27' y1='46.11' x2='185.04' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='216.51' y1='46.11' x2='216.51' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='216.51' y1='46.11' x2='216.51' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='197.63,46.11 197.63,46.11 235.40,46.11 235.40,46.11 197.63,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00BA38; fill-opacity: 0.85;' />
<line x1='197.63' y1='46.11' x2='235.40' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='266.87' y1='46.11' x2='266.87' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='266.87' y1='46.11' x2='266.87' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='247.99,46.11 247.99,46.11 285.75,46.11 285.75,46.11 247.99,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00C19F; fill-opacity: 0.85;' />
<line x1='247.99' y1='46.11' x2='285.75' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='317.23' y1='46.11' x2='317.23' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='317.23' y1='46.11' x2='317.23' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='298.34,46.11 298.34,46.11 336.11,46.11 336.11,46.11 298.34,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00B9E3; fill-opacity: 0.85;' />
<line x1='298.34' y1='46.11' x2='336.11' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='367.59' y1='46.11' x2='367.59' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='367.59' y1='46.11' x2='367.59' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='348.70,46.11 348.70,46.11 386.47,46.11 386.47,46.11 348.70,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #619CFF; fill-opacity: 0.85;' />
<line x1='348.70' y1='46.11' x2='386.47' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='417.95' y1='46.11' x2='417.95' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='417.95' y1='46.11' x2='417.95' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='399.06,46.11 399.06,46.11 436.83,46.11 436.83,46.11 399.06,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #DB72FB; fill-opacity: 0.85;' />
<line x1='399.06' y1='46.11' x2='436.83' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='468.31' y1='46.11' x2='468.31' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='468.31' y1='46.11' x2='468.31' y2='46.11' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='449.42,46.11 449.42,46.11 487.19,46.11 487.19,46.11 449.42,46.11 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #FF61C3; fill-opacity: 0.85;' />
<line x1='449.42' y1='46.11' x2='487.19' y2='46.11' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<rect x='35.22' y='39.42' width='463.30' height='147.21' style='stroke-width: 1.07; stroke: #333333;' />
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuMjJ8NDk4LjUyfDIwOC43NnwzNTUuOTc='>
<rect x='35.22' y='208.76' width='463.30' height='147.21' />
</clipPath>
</defs>
<g clip-path='url(#cpMzUuMjJ8NDk4LjUyfDIwOC43NnwzNTUuOTc=)'>
<rect x='35.22' y='208.76' width='463.30' height='147.21' style='stroke-width: 1.07; stroke: none; fill: #FFFFFF;' />
<polyline points='35.22,328.06 498.52,328.06 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,285.62 498.52,285.62 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,243.18 498.52,243.18 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,349.27 498.52,349.27 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,306.84 498.52,306.84 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,264.40 498.52,264.40 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,221.96 498.52,221.96 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='65.43,355.97 65.43,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='115.79,355.97 115.79,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='166.15,355.97 166.15,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='216.51,355.97 216.51,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='266.87,355.97 266.87,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='317.23,355.97 317.23,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='367.59,355.97 367.59,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='417.95,355.97 417.95,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='468.31,355.97 468.31,208.76 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<line x1='65.43' y1='349.27' x2='65.43' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='65.43' y1='349.27' x2='65.43' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='46.55,349.27 46.55,349.27 84.32,349.27 84.32,349.27 46.55,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #F8766D; fill-opacity: 0.85;' />
<line x1='46.55' y1='349.27' x2='84.32' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='115.79' y1='349.27' x2='115.79' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='115.79' y1='349.27' x2='115.79' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='96.91,349.27 96.91,349.27 134.68,349.27 134.68,349.27 96.91,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #D39200; fill-opacity: 0.85;' />
<line x1='96.91' y1='349.27' x2='134.68' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='166.15' y1='349.27' x2='166.15' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='166.15' y1='349.27' x2='166.15' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='147.27,349.27 147.27,349.27 185.04,349.27 185.04,349.27 147.27,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #93AA00; fill-opacity: 0.85;' />
<line x1='147.27' y1='349.27' x2='185.04' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='216.51' y1='349.27' x2='216.51' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='216.51' y1='349.27' x2='216.51' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='197.63,349.27 197.63,349.27 235.40,349.27 235.40,349.27 197.63,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00BA38; fill-opacity: 0.85;' />
<line x1='197.63' y1='349.27' x2='235.40' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='266.87' y1='349.27' x2='266.87' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='266.87' y1='349.27' x2='266.87' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='247.99,349.27 247.99,349.27 285.75,349.27 285.75,349.27 247.99,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00C19F; fill-opacity: 0.85;' />
<line x1='247.99' y1='349.27' x2='285.75' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='317.23' y1='349.27' x2='317.23' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='317.23' y1='349.27' x2='317.23' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='298.34,349.27 298.34,349.27 336.11,349.27 336.11,349.27 298.34,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00B9E3; fill-opacity: 0.85;' />
<line x1='298.34' y1='349.27' x2='336.11' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='367.59' y1='349.27' x2='367.59' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='367.59' y1='349.27' x2='367.59' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='348.70,349.27 348.70,349.27 386.47,349.27 386.47,349.27 348.70,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #619CFF; fill-opacity: 0.85;' />
<line x1='348.70' y1='349.27' x2='386.47' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='417.95' y1='349.27' x2='417.95' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='417.95' y1='349.27' x2='417.95' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='399.06,349.27 399.06,349.27 436.83,349.27 436.83,349.27 399.06,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #DB72FB; fill-opacity: 0.85;' />
<line x1='399.06' y1='349.27' x2='436.83' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='468.31' y1='349.27' x2='468.31' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='468.31' y1='349.27' x2='468.31' y2='349.27' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='449.42,349.27 449.42,349.27 487.19,349.27 487.19,349.27 449.42,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #FF61C3; fill-opacity: 0.85;' />
<line x1='449.42' y1='349.27' x2='487.19' y2='349.27' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<rect x='35.22' y='208.76' width='463.30' height='147.21' style='stroke-width: 1.07; stroke: #333333;' />
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuMjJ8NDk4LjUyfDM3OC4xMHw1MjUuMzE='>
<rect x='35.22' y='378.10' width='463.30' height='147.21' />
</clipPath>
</defs>
<g clip-path='url(#cpMzUuMjJ8NDk4LjUyfDM3OC4xMHw1MjUuMzE=)'>
<rect x='35.22' y='378.10' width='463.30' height='147.21' style='stroke-width: 1.07; stroke: none; fill: #FFFFFF;' />
<polyline points='35.22,497.39 498.52,497.39 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,454.96 498.52,454.96 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,412.52 498.52,412.52 ' style='stroke-width: 0.53; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,518.61 498.52,518.61 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,476.18 498.52,476.18 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,433.74 498.52,433.74 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='35.22,391.30 498.52,391.30 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='65.43,525.31 65.43,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='115.79,525.31 115.79,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='166.15,525.31 166.15,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='216.51,525.31 216.51,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='266.87,525.31 266.87,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='317.23,525.31 317.23,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='367.59,525.31 367.59,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='417.95,525.31 417.95,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<polyline points='468.31,525.31 468.31,378.10 ' style='stroke-width: 1.07; stroke: #EBEBEB; stroke-linecap: butt;' />
<line x1='65.43' y1='518.61' x2='65.43' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='65.43' y1='518.61' x2='65.43' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='46.55,518.61 46.55,518.61 84.32,518.61 84.32,518.61 46.55,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #F8766D; fill-opacity: 0.85;' />
<line x1='46.55' y1='518.61' x2='84.32' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='115.79' y1='518.61' x2='115.79' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='115.79' y1='518.61' x2='115.79' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='96.91,518.61 96.91,518.61 134.68,518.61 134.68,518.61 96.91,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #D39200; fill-opacity: 0.85;' />
<line x1='96.91' y1='518.61' x2='134.68' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='166.15' y1='518.61' x2='166.15' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='166.15' y1='518.61' x2='166.15' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='147.27,518.61 147.27,518.61 185.04,518.61 185.04,518.61 147.27,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #93AA00; fill-opacity: 0.85;' />
<line x1='147.27' y1='518.61' x2='185.04' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='216.51' y1='518.61' x2='216.51' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='216.51' y1='518.61' x2='216.51' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='197.63,518.61 197.63,518.61 235.40,518.61 235.40,518.61 197.63,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00BA38; fill-opacity: 0.85;' />
<line x1='197.63' y1='518.61' x2='235.40' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='266.87' y1='518.61' x2='266.87' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='266.87' y1='518.61' x2='266.87' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='247.99,518.61 247.99,518.61 285.75,518.61 285.75,518.61 247.99,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00C19F; fill-opacity: 0.85;' />
<line x1='247.99' y1='518.61' x2='285.75' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='317.23' y1='518.61' x2='317.23' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='317.23' y1='518.61' x2='317.23' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='298.34,518.61 298.34,518.61 336.11,518.61 336.11,518.61 298.34,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #00B9E3; fill-opacity: 0.85;' />
<line x1='298.34' y1='518.61' x2='336.11' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='367.59' y1='518.61' x2='367.59' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='367.59' y1='518.61' x2='367.59' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='348.70,518.61 348.70,518.61 386.47,518.61 386.47,518.61 348.70,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #619CFF; fill-opacity: 0.85;' />
<line x1='348.70' y1='518.61' x2='386.47' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='417.95' y1='518.61' x2='417.95' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='417.95' y1='518.61' x2='417.95' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='399.06,518.61 399.06,518.61 436.83,518.61 436.83,518.61 399.06,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #DB72FB; fill-opacity: 0.85;' />
<line x1='399.06' y1='518.61' x2='436.83' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<line x1='468.31' y1='518.61' x2='468.31' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<line x1='468.31' y1='518.61' x2='468.31' y2='518.61' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polygon points='449.42,518.61 449.42,518.61 487.19,518.61 487.19,518.61 449.42,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter; fill: #FF61C3; fill-opacity: 0.85;' />
<line x1='449.42' y1='518.61' x2='487.19' y2='518.61' style='stroke-width: 2.13; stroke: #333333; stroke-linecap: butt; stroke-linejoin: miter;' />
<rect x='35.22' y='378.10' width='463.30' height='147.21' style='stroke-width: 1.07; stroke: #333333;' />
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuMjJ8NDk4LjUyfDM2MS40NXwzNzguMTA='>
<rect x='35.22' y='361.45' width='463.30' height='16.65' />
</clipPath>
</defs>
<g clip-path='url(#cpMzUuMjJ8NDk4LjUyfDM2MS40NXwzNzguMTA=)'>
<rect x='35.22' y='361.45' width='463.30' height='16.65' style='stroke-width: 1.07; stroke: #333333; fill: #D9D9D9;' />
<text x='266.87' y='372.80' text-anchor='middle' style='font-size: 8.80px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='27.88px' lengthAdjust='spacingAndGlyphs'>riscv64</text>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuMjJ8NDk4LjUyfDE5Mi4xMXwyMDguNzY='>
<rect x='35.22' y='192.11' width='463.30' height='16.65' />
</clipPath>
</defs>
<g clip-path='url(#cpMzUuMjJ8NDk4LjUyfDE5Mi4xMXwyMDguNzY=)'>
<rect x='35.22' y='192.11' width='463.30' height='16.65' style='stroke-width: 1.07; stroke: #333333; fill: #D9D9D9;' />
<text x='266.87' y='203.46' text-anchor='middle' style='font-size: 8.80px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='31.78px' lengthAdjust='spacingAndGlyphs'>aarch64</text>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
</g>
<defs>
<clipPath id='cpMzUuMjJ8NDk4LjUyfDIyLjc3fDM5LjQy'>
<rect x='35.22' y='22.77' width='463.30' height='16.65' />
</clipPath>
</defs>
<g clip-path='url(#cpMzUuMjJ8NDk4LjUyfDIyLjc3fDM5LjQy)'>
<rect x='35.22' y='22.77' width='463.30' height='16.65' style='stroke-width: 1.07; stroke: #333333; fill: #D9D9D9;' />
<text x='266.87' y='34.12' text-anchor='middle' style='font-size: 8.80px;fill: #1A1A1A; font-family: "Liberation Sans";' textLength='26.89px' lengthAdjust='spacingAndGlyphs'>amd64</text>
</g>
<g clip-path='url(#cpMC4wMHw1MDQuMDB8MC4wMHw1NzYuMDA=)'>
<polyline points='65.43,528.05 65.43,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='115.79,528.05 115.79,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='166.15,528.05 166.15,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='216.51,528.05 216.51,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='266.87,528.05 266.87,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='317.23,528.05 317.23,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='367.59,528.05 367.59,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='417.95,528.05 417.95,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='468.31,528.05 468.31,525.31 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<text transform='translate(69.72,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='18.59px' lengthAdjust='spacingAndGlyphs'>zuse</text>
<text transform='translate(120.07,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='31.28px' lengthAdjust='spacingAndGlyphs'>rajames</text>
<text transform='translate(170.43,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>00</text>
<text transform='translate(220.79,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>01</text>
<text transform='translate(271.15,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>02</text>
<text transform='translate(321.51,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>03</text>
<text transform='translate(371.87,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>04</text>
<text transform='translate(422.23,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>05</text>
<text transform='translate(472.59,534.52) rotate(-45)' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='9.78px' lengthAdjust='spacingAndGlyphs'>06</text>
<text x='30.29' y='182.96' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.0</text>
<text x='30.29' y='140.52' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.2</text>
<text x='30.29' y='98.09' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.4</text>
<text x='30.29' y='55.65' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.6</text>
<polyline points='32.48,179.94 35.22,179.94 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,137.50 35.22,137.50 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,95.06 35.22,95.06 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,52.62 35.22,52.62 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<text x='30.29' y='352.30' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.0</text>
<text x='30.29' y='309.86' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.2</text>
<text x='30.29' y='267.42' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.4</text>
<text x='30.29' y='224.99' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.6</text>
<polyline points='32.48,349.27 35.22,349.27 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,306.84 35.22,306.84 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,264.40 35.22,264.40 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,221.96 35.22,221.96 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<text x='30.29' y='521.64' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.0</text>
<text x='30.29' y='479.20' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.2</text>
<text x='30.29' y='436.76' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.4</text>
<text x='30.29' y='394.33' text-anchor='end' style='font-size: 8.80px;fill: #4D4D4D; font-family: "Liberation Sans";' textLength='12.22px' lengthAdjust='spacingAndGlyphs'>0.6</text>
<polyline points='32.48,518.61 35.22,518.61 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,476.18 35.22,476.18 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,433.74 35.22,433.74 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<polyline points='32.48,391.30 35.22,391.30 ' style='stroke-width: 1.07; stroke: #333333; stroke-linecap: butt;' />
<text x='266.87' y='568.24' text-anchor='middle' style='font-size: 11.00px; font-family: "Liberation Sans";' textLength='35.50px' lengthAdjust='spacingAndGlyphs'>Identity</text>
<text transform='translate(13.05,282.36) rotate(-90)' text-anchor='middle' style='font-size: 11.00px; font-family: "Liberation Sans";' textLength='77.25px' lengthAdjust='spacingAndGlyphs'>Timing variance</text>
<text x='35.22' y='14.55' style='font-size: 13.20px; font-family: "Liberation Sans";' textLength='92.58px' lengthAdjust='spacingAndGlyphs'>Timing variance</text>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 33 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 38 KiB

File diff suppressed because it is too large Load Diff

After

Width:  |  Height:  |  Size: 35 KiB