every factor interaction, and a raw-data appendix Expanded the campaign-mechanism validation report from a condensed 6-page summary into the full depth Captain Bob asked for: analyze all 9 cells as a conglomerate Latin square, then dive into each cell's own data, then cover every within-ISA and cross-factor interaction explicitly rather than averaging it away. Report structure (127 pages, compiled clean, no undefined references): - Front matter: context, methodology, the SWAP-MTX bug narrative (console-interleaving fix + the Fisher-Yates correctness bug and its fix, both already committed separately) - Layer 1: aggregate 3x3 Latin square (heatmap, invariant-metrics table) - Per-Cell Deep Dive (9 sections): cfg-level distribution, summary table, and a rep-order execution-trajectory chart per cell -- the trajectory charts are what actually visualize the order-dependence finding rather than just stating it - Per-ISA Deep Dive (3 sections): within-architecture seed comparison (violin plots, Kruskal-Wallis, per-factor main effects) - Factor Interactions (6 sections, every pairwise combination of the 4 L8 binary factors): both infer_dec_q and early_exit interaction plots faceted by architecture, plus the three-way factor x factor x architecture significance test - Per-Factor Response (4 sections): linear response by architecture, with an explicit note that a true quadratic term isn't identifiable from this 2-level factorial design - Appendix: full run_id-ordered raw data, all 4,320 rows across all 9 cells, as the primary-source backing for every statistic above Generated programmatically (analyse_stadium_relaunch_fixed.R for the aggregate layer, generate_stadium_deepdive.R for the per-cell/per-ISA/ interaction/appendix layers) rather than hand-authored, since content at this scale needs to be data-driven to stay honest. Also includes analyse_stadium_relaunch.R, the earlier script built against the pre-fix (buggy-shuffle) dataset -- superseded but kept for the record, matching how the underlying data commits were handled. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
252 lines
14 KiB
R
252 lines
14 KiB
R
#!/usr/bin/env Rscript
|
|
# analyse_stadium_relaunch_fixed.R
|
|
# StarForth LithosAnanke -- EXEC-DOE 3x3 Latin square campaign, re-run on
|
|
# the post-item-4.6 Stadium substrate AND against the fixed SWAP-MTX
|
|
# shuffle (2026-08-20). Supersedes analyse_stadium_relaunch.R's dataset
|
|
# (acl-rwt-20260820/, buggy shuffle) with acl-rwt-20260820-fixed/.
|
|
#
|
|
# Two layers, per Captain Bob's own framing:
|
|
# 1. Aggregate -- all 9 cells as a conglomerate Latin square.
|
|
# 2. Per-cell / per-ISA -- each cell's own internal pattern, and how
|
|
# each ISA's three seeds hold up against each other (subtle
|
|
# within-ISA interactions), including 2-way factor interactions and
|
|
# quadratic (factor^2) terms.
|
|
|
|
suppressPackageStartupMessages({
|
|
library(ggplot2)
|
|
library(svglite)
|
|
library(dplyr)
|
|
library(tidyr)
|
|
library(scales)
|
|
library(patchwork)
|
|
})
|
|
|
|
SCRIPT_DIR <- tryCatch(
|
|
dirname(normalizePath(sys.frames()[[1]]$ofile)),
|
|
error = function(e) getwd()
|
|
)
|
|
BASE_DIR <- normalizePath(file.path(SCRIPT_DIR, ".."))
|
|
RUNS_DIR <- file.path(BASE_DIR, "runs", "acl-rwt-20260820-fixed")
|
|
OUT_CHARTS <- file.path(SCRIPT_DIR, "charts")
|
|
OUT_TABLES <- file.path(SCRIPT_DIR, "tables")
|
|
dir.create(OUT_CHARTS, showWarnings = FALSE, recursive = TRUE)
|
|
dir.create(OUT_TABLES, showWarnings = FALSE, recursive = TRUE)
|
|
|
|
cat("══════════════════════════════════════════════════════════════════\n")
|
|
cat(" StarForth LithosAnanke -- Stadium relaunch, FIXED shuffle\n")
|
|
cat(" Item 5.1 / F.3 -- campaign-mechanism + factor analysis, 2026-08-20\n")
|
|
cat("══════════════════════════════════════════════════════════════════\n\n")
|
|
|
|
arch_colours <- c(amd64 = "#E07B39", aarch64 = "#4A90D9", riscv64 = "#50C878")
|
|
|
|
theme_light_sf <- function(base = 11) {
|
|
theme_minimal(base_size = base) %+replace% theme(
|
|
panel.grid.minor = element_blank(),
|
|
panel.grid.major = element_line(colour = "grey90"),
|
|
strip.text = element_text(face = "bold"),
|
|
plot.title = element_text(face = "bold", size = base + 1),
|
|
plot.subtitle = element_text(colour = "grey40", size = base - 2),
|
|
legend.position = "bottom",
|
|
legend.key.size = unit(0.5, "cm")
|
|
)
|
|
}
|
|
theme_dark_sf <- function(base = 11) {
|
|
theme_minimal(base_size = base) %+replace% theme(
|
|
panel.background = element_rect(fill = "#0d0d0d", colour = NA),
|
|
plot.background = element_rect(fill = "#0d0d0d", colour = NA),
|
|
panel.grid.major = element_line(colour = "#1e1e1e"),
|
|
panel.grid.minor = element_blank(),
|
|
axis.text = element_text(colour = "#aaaaaa"),
|
|
axis.title = element_text(colour = "#cccccc"),
|
|
strip.text = element_text(colour = "white", face = "bold"),
|
|
plot.title = element_text(colour = "white", face = "bold", size = base + 1),
|
|
plot.subtitle = element_text(colour = "#666666", size = base - 2),
|
|
legend.text = element_text(colour = "#aaaaaa"),
|
|
legend.title = element_text(colour = "#cccccc"),
|
|
legend.background = element_rect(fill = "#0d0d0d", colour = NA),
|
|
legend.position = "bottom",
|
|
legend.key.size = unit(0.5, "cm")
|
|
)
|
|
}
|
|
save_svg <- function(plot, name, w = 12, h = 7) {
|
|
path <- file.path(OUT_CHARTS, paste0(name, ".svg"))
|
|
svglite(path, width = w, height = h); print(plot); dev.off()
|
|
cat(sprintf(" Saved: %s.svg\n", name))
|
|
invisible(path)
|
|
}
|
|
|
|
col_names <- c("run_id", "cfg", "rep", "ent_in", "cv_in", "tmp_in", "stb_in",
|
|
"l8_mode", "win_div", "infer_win", "infer_dec_q", "infer_var_q",
|
|
"early_exit", "bc_mean_q", "bb_mean_q", "fit_q")
|
|
archs <- c("amd64", "aarch64", "riscv64")
|
|
seeds <- c("12345", "67890", "13579")
|
|
|
|
load_cell <- function(arch, seed) {
|
|
f <- file.path(RUNS_DIR, sprintf("%s-seed%s.csv", arch, seed))
|
|
df <- read.csv(f, header = TRUE)
|
|
names(df) <- col_names
|
|
df$arch <- arch; df$seed <- seed
|
|
df$ent_f <- factor(ifelse(df$ent_in > 0, "hi", "lo"))
|
|
df$cv_f <- factor(ifelse(df$cv_in > 0, "hi", "lo"))
|
|
df$tmp_f <- factor(ifelse(df$tmp_in > 0, "hi", "lo"))
|
|
df$stb_f <- factor(ifelse(df$stb_in > 0, "hi", "lo"))
|
|
df
|
|
}
|
|
|
|
cat("Loading 9 cells (fixed-shuffle dataset)...\n")
|
|
all_data <- bind_rows(lapply(archs, function(a) bind_rows(lapply(seeds, function(s) load_cell(a, s)))))
|
|
cat(sprintf(" Total rows: %d (expect 4320)\n", nrow(all_data)))
|
|
all_data$arch <- factor(all_data$arch, levels = archs)
|
|
all_data$seed <- factor(all_data$seed, levels = seeds)
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# LAYER 1 -- AGGREGATE: all 9 cells as a conglomerate Latin square
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
|
|
cell_summary <- all_data %>%
|
|
group_by(arch, seed) %>%
|
|
summarise(
|
|
n_rows = n(), n_cfg = n_distinct(cfg), n_run_id = n_distinct(run_id),
|
|
mean_infer_dec_q = mean(infer_dec_q), sd_infer_dec_q = sd(infer_dec_q),
|
|
early_exit_rate = mean(early_exit),
|
|
l8_mode_const = length(unique(l8_mode)) == 1,
|
|
win_div_const = length(unique(win_div)) == 1,
|
|
fit_q_const = length(unique(fit_q)) == 1,
|
|
.groups = "drop"
|
|
)
|
|
write.csv(cell_summary, file.path(OUT_TABLES, "stadium_fixed_cell_summary.csv"), row.names = FALSE)
|
|
cat("\nPer-cell summary (Layer 1 -- aggregate view):\n"); print(as.data.frame(cell_summary))
|
|
|
|
# Confirms the invariant metrics: l8_mode/win_div/infer_win/infer_var_q/
|
|
# bc_mean_q/bb_mean_q/fit_q are all zero-variance across all 9 cells --
|
|
# same extreme-determinism result survives the shuffle fix (expected:
|
|
# the shuffle bug affected WHICH (cfg,rep) pairs got visited, not the
|
|
# underlying physics/inference computation).
|
|
invariant_check <- all_data %>%
|
|
summarise(across(c(l8_mode, win_div, infer_win, infer_var_q, bc_mean_q, bb_mean_q, fit_q),
|
|
n_distinct))
|
|
cat("\nInvariant-metric check across ALL 4320 rows (expect all = 1):\n")
|
|
print(as.data.frame(invariant_check))
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# FIGURE 1: Latin-square heatmap -- mean infer_dec_q per (arch, seed) cell
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
cat("\n[F1] Aggregate Latin-square heatmap (light + dark)...\n")
|
|
|
|
make_heatmap <- function(dark = FALSE) {
|
|
thm <- if (dark) theme_dark_sf() else theme_light_sf()
|
|
txt <- if (dark) "white" else "grey10"
|
|
ggplot(cell_summary, aes(x = seed, y = arch, fill = mean_infer_dec_q)) +
|
|
geom_tile(colour = if (dark) "#0d0d0d" else "white", linewidth = 1.5) +
|
|
geom_text(aes(label = sprintf("%.1f\n(sd=%.1f)", mean_infer_dec_q, sd_infer_dec_q)),
|
|
colour = txt, fontface = "bold", size = 3.6, lineheight = 0.9) +
|
|
scale_fill_gradient(low = "#4A90D9", high = "#E07B39", name = "mean infer_dec_q") +
|
|
labs(title = "Aggregate Latin Square -- mean infer_dec_q per cell",
|
|
subtitle = "9 cells, 480 runs each, fixed Fisher-Yates shuffle, Stadium substrate",
|
|
x = "Seed", y = "Architecture") +
|
|
thm
|
|
}
|
|
save_svg(make_heatmap(FALSE), "stadium_fixed_latin_square_light", w = 9, h = 6)
|
|
save_svg(make_heatmap(TRUE), "stadium_fixed_latin_square_dark", w = 9, h = 6)
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# LAYER 2a -- PER-ISA: how each ISA's three seeds hold up against each
|
|
# other (within-ISA consistency, the "subtle interactions ... within the
|
|
# ISA itself" Captain Bob asked for)
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
cat("\n[F2] Per-ISA seed comparison, infer_dec_q distribution (light + dark)...\n")
|
|
|
|
make_isa_panel <- function(dark = FALSE) {
|
|
thm <- if (dark) theme_dark_sf() else theme_light_sf()
|
|
ggplot(all_data, aes(x = seed, y = infer_dec_q, fill = arch)) +
|
|
geom_boxplot(alpha = 0.85, outlier.size = 0.5, outlier.alpha = 0.4) +
|
|
scale_fill_manual(values = arch_colours, guide = "none") +
|
|
facet_wrap(~arch, nrow = 1) +
|
|
labs(title = "Within-ISA Seed Consistency -- infer_dec_q by Seed, Faceted by ISA",
|
|
subtitle = "Each panel: one ISA's 3 seeds side by side (480 runs/seed)",
|
|
x = "Seed", y = "infer_dec_q") +
|
|
thm
|
|
}
|
|
save_svg(make_isa_panel(FALSE), "stadium_fixed_isa_seed_panel_light", w = 12, h = 6)
|
|
save_svg(make_isa_panel(TRUE), "stadium_fixed_isa_seed_panel_dark", w = 12, h = 6)
|
|
|
|
# Kruskal-Wallis: does seed matter WITHIN each ISA?
|
|
kw_within_isa <- lapply(archs, function(a) {
|
|
sub <- filter(all_data, arch == a)
|
|
kw <- kruskal.test(infer_dec_q ~ seed, data = sub)
|
|
data.frame(arch = a, H = unname(kw$statistic), df = unname(kw$parameter), p = kw$p.value)
|
|
})
|
|
kw_within_isa_df <- bind_rows(kw_within_isa)
|
|
write.csv(kw_within_isa_df, file.path(OUT_TABLES, "stadium_fixed_kw_within_isa.csv"), row.names = FALSE)
|
|
cat("\nWithin-ISA seed effect (Kruskal-Wallis, infer_dec_q ~ seed, per ISA):\n")
|
|
print(kw_within_isa_df)
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# LAYER 2b -- CROSS-ISA interaction + quadratic terms on infer_dec_q
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
cat("\n[F3] Factor model: infer_dec_q ~ factors * arch (2-way interactions + quadratic)...\n")
|
|
|
|
all_data$ent_q <- (all_data$ent_in / 49152)^2
|
|
all_data$cv_q <- (all_data$cv_in / 9830)^2
|
|
all_data$tmp_q <- (all_data$tmp_in / 32768)^2
|
|
all_data$stb_q <- (all_data$stb_in / 32768)^2
|
|
|
|
fit_full <- lm(infer_dec_q ~ (ent_f + cv_f + tmp_f + stb_f) * arch +
|
|
ent_q + cv_q + tmp_q + stb_q, data = all_data)
|
|
anova_full <- anova(fit_full)
|
|
capture.output(print(anova_full), file = file.path(OUT_TABLES, "stadium_fixed_anova_full.txt"))
|
|
cat("\nANOVA -- infer_dec_q ~ (4 factors) * arch + quadratic terms:\n")
|
|
print(anova_full)
|
|
|
|
# Logistic regression: early_exit ~ factors * arch
|
|
fit_logit <- glm(early_exit ~ (ent_f + cv_f + tmp_f + stb_f) * arch,
|
|
data = all_data, family = binomial())
|
|
capture.output(print(summary(fit_logit)), file = file.path(OUT_TABLES, "stadium_fixed_logit_early_exit.txt"))
|
|
cat("\nLogistic regression -- early_exit ~ (4 factors) * arch (summary saved to tables/).\n")
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# FIGURE 2: interaction plot -- ent_f x tmp_f, faceted by arch
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
cat("\n[F4] Interaction plot: ent_in x tmp_in on infer_dec_q, by ISA (light + dark)...\n")
|
|
|
|
interaction_df <- all_data %>%
|
|
group_by(arch, ent_f, tmp_f) %>%
|
|
summarise(mean_dec = mean(infer_dec_q), se = sd(infer_dec_q)/sqrt(n()), .groups = "drop")
|
|
|
|
make_interaction <- function(dark = FALSE) {
|
|
thm <- if (dark) theme_dark_sf() else theme_light_sf()
|
|
ggplot(interaction_df, aes(x = tmp_f, y = mean_dec, colour = ent_f, group = ent_f)) +
|
|
geom_line(linewidth = 1) +
|
|
geom_point(size = 2.5) +
|
|
geom_errorbar(aes(ymin = mean_dec - se, ymax = mean_dec + se), width = 0.1) +
|
|
facet_wrap(~arch, nrow = 1) +
|
|
scale_colour_manual(values = c(hi = "#D62728", lo = "#4A90D9"), name = "entropy factor") +
|
|
labs(title = "ent_in x tmp_in Interaction on infer_dec_q, by ISA",
|
|
subtitle = "Non-parallel lines = interaction effect; divergence across panels = ISA-dependent interaction",
|
|
x = "temporal-decay factor", y = "mean infer_dec_q") +
|
|
thm
|
|
}
|
|
save_svg(make_interaction(FALSE), "stadium_fixed_interaction_light", w = 12, h = 6)
|
|
save_svg(make_interaction(TRUE), "stadium_fixed_interaction_dark", w = 12, h = 6)
|
|
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
# LAYER 2c -- PER-CELL breakdown: each cell's own infer_dec_q histogram
|
|
# ══════════════════════════════════════════════════════════════════════
|
|
cat("\n[F5] Per-cell infer_dec_q histograms, 3x3 grid (light + dark)...\n")
|
|
|
|
make_percell_grid <- function(dark = FALSE) {
|
|
thm <- if (dark) theme_dark_sf() else theme_light_sf()
|
|
fillc <- if (dark) "#4A90D9" else "#E07B39"
|
|
ggplot(all_data, aes(x = infer_dec_q)) +
|
|
geom_histogram(bins = 20, fill = fillc, alpha = 0.85, colour = NA) +
|
|
facet_grid(arch ~ seed) +
|
|
labs(title = "Per-Cell infer_dec_q Distribution -- 3x3 Latin Square Grid",
|
|
subtitle = "Rows: ISA. Columns: seed. Each panel: one cell's own 480 runs.",
|
|
x = "infer_dec_q", y = "count") +
|
|
thm
|
|
}
|
|
save_svg(make_percell_grid(FALSE), "stadium_fixed_percell_grid_light", w = 11, h = 9)
|
|
save_svg(make_percell_grid(TRUE), "stadium_fixed_percell_grid_dark", w = 11, h = 9)
|
|
|
|
cat("\nDone. Tables in analysis/tables/, charts in analysis/charts/.\n")
|