---
title: "DDESONN — Plot Controls — Scenario 1 & 2 — Single Run: Scenario A"
output:
  rmarkdown::html_vignette:
    toc: true
    toc_depth: 2
    self_contained: false
vignette: >
  %\VignetteIndexEntry{DDESONN — Plot Controls — Scenario 1 & 2 — Single Run: Scenario A}
  %\VignetteEngine{knitr::rmarkdown}
  %\VignetteEncoding{UTF-8}
---

## Plot Interface — Scenario 1 vs Scenario 2

This vignette demonstrates two supported plot configuration interfaces in DDESONN.

---

### Scenario 1 — Independent Call Sites (`training_overrides`)

Scenario 1 configures plotting through **three independent call sites** inside `training_overrides`:

1. `training_overrides$per_epoch_plots`
2. `training_overrides$performance_relevance`
3. `training_overrides$evaluate_predictions_report_plots`

Each plot category is configured separately.  
This represents the minimal integration bridge interface.

For the unified umbrella interface (recommended), see:

**`plot-controls_scenario1-2_single-run_scenarioA.Rmd`**

---

### Scenario 2 — Unified Umbrella (`plot_controls`)

Scenario 2 configures all plots through a **single unified interface**:

- `plot_controls`

All plot categories (e.g., `per_epoch`, `performance_relevance`, `evaluate_report`) are defined under this one umbrella.

For comparison with the three-call-site structure (Scenario 1), see:

**`plot-controls_scenario1-2_single-run_scenarioA.Rmd`**

```{r setup, include=FALSE}
is_check_env <- nzchar(Sys.getenv("_R_CHECK_PACKAGE_NAME_"))
.vig_tmp_root <- file.path(tempdir(), "ddesonn-vig-s1-s2")
dir.create(.vig_tmp_root, recursive = TRUE, showWarnings = FALSE)
options(DDESONN_OUTPUT_ROOT = .vig_tmp_root)
Sys.setenv(DDESONN_ARTIFACTS_ROOT = .vig_tmp_root)
# ============================================================
# FILE: vignettes/plot-controls_scenario1-2_single-run_scenarioA.Rmd
# FULL WORKING — DDESONN Plot Controls — Scenario 1 & 2 — Single Run: Scenario A   
#
# GOAL:
# - Demonstrate TWO supported user-facing interfaces for plot control:
#   (1) Scenario 1: training_overrides knobs (minimal integration)
#   (2) Scenario 2: plot_controls umbrella (recommended)
#
# IMPORTANT (binary eval):
# - To emit BOTH confusion matrix heatmaps (fixed + tuned),
#   users must set:
#     accuracy_plot = TRUE
#     accuracy_plot_mode = "both"
#
#  IMPORTANT (performance/relevance):
# - This vignette uses the *current* naming shown in your branch:
#     plot_controls$performance_relevance
#   and Scenario 1 uses the same key so the bridge can map it.
# - No "boxplot" naming anywhere in config.                   
# ============================================================

# ============================================================
#  FIX: VIGNETTE PARSE-SAFE opts_chunk$set
# ============================================================
# knitr options (SINGLE consolidated set)
# ============================================================
knitr::opts_chunk$set(                                                      
  echo = TRUE,                                                              
  message = FALSE,                                                         
  warning = FALSE,                                                         
  fig.width = 6.5,
  fig.height = 4,
  dpi = 96,
  fig.retina = 1,                                                           
  out.width = "85%",                                                        
  fig.path = file.path(.vig_tmp_root, "figures", ""),
  cache.path = file.path(.vig_tmp_root, "cache", "")
)                                                                                   

# ============================================================
#  FIX: VIGNETTE-SAFE LOAD (no devtools::load_all)
# ============================================================
if (!requireNamespace("DDESONN", quietly = TRUE)) {
  stop(
    "DDESONN must be installed to build this vignette. ",
    "Run: install.packages('DDESONN') (or your install flow) then rebuild vignettes.",
    call. = FALSE
  )
}

library(DDESONN)

## ============================================================  
## SECTION: Recommended knitr figure options (consistency)       
## ============================================================  
## For consistent ggplot title sizing across HTML/PDF output,    
## keep these chunk options stable:                              
## - fig.width / fig.height set explicit device size (inches).   
## - fig.retina controls display scaling (avoid implicit CSS).   
## - out.width affects HTML scaling; keep constant across plots. 

# ============================================================
# Output roots for vignette artifacts
# - Keep artifact paths short and outside vignette *_files trees.
# - Disable persistent plot saving during R CMD check.
# ============================================================
.dd_out_root <- file.path(.vig_tmp_root, "outputs")
out1 <- file.path(.dd_out_root, "DDESONN_plots_scenarioA_s1")                               
out2 <- file.path(.dd_out_root, "DDESONN_plots_scenarioA_s2")                               

if (!is_check_env) {
  dir.create(out1, recursive = TRUE, showWarnings = FALSE)
  dir.create(out2, recursive = TRUE, showWarnings = FALSE)
}

# Make package helpers resolve to a short temp root.
options(DDESONN_OUTPUT_ROOT = if (is_check_env) .vig_tmp_root else out1)
Sys.setenv(DDESONN_ARTIFACTS_ROOT = if (is_check_env) .vig_tmp_root else out1)

# ============================================================
# Helper: include_saved_plots()
# - Uses knitr::image_uri() to embed PNGs as data: URIs
# ============================================================
include_saved_plots <- function(output_root, header) {

  plot_dir <- ddesonn_plots_dir(output_root)

  if (!dir.exists(plot_dir)) {
    return(knitr::asis_output(
      paste0("\n\n[plot-controls:scenarioA] plots dir does not exist: ", plot_dir, "\n\n")  
    ))
  }

  pngs <- list.files(plot_dir, pattern = "\\.png$", recursive = TRUE, full.names = TRUE)
  pngs <- pngs[nzchar(pngs)]
  pngs <- pngs[order(pngs)]

  if (!length(pngs)) {
    return(knitr::asis_output(
      paste0("\n\n[plot-controls:scenarioA] no PNGs found under: ", plot_dir, "\n\n")       
    ))
  }

  out <- character()
  out <- c(out, paste0("\n\n## ", header, "\n\n"))

  for (p in pngs) {
    uri <- knitr::image_uri(p)
    out <- c(out, paste0('<img src="', uri, '" style="width:900px; max-width:100%; height:auto;" />\n\n'))  
    out <- c(out, "<br><br>\n\n")                                                                            
  }

  knitr::asis_output(paste(out, collapse = ""))
}

## What “both heatmaps” means (binary EvaluatePredictionsReport)
## (unchanged text section)
set.seed(111)

ext_dir <- system.file("extdata", package = "DDESONN")
if (!nzchar(ext_dir)) stop("Could not find DDESONN extdata folder.", call. = FALSE)

hf_path <- file.path(ext_dir, "heart_failure_clinical_records.csv")
if (!file.exists(hf_path)) {
  csvs <- list.files(ext_dir, pattern = "\\.csv$", full.names = TRUE)
  if (!length(csvs)) stop("No .csv files found in extdata.", call. = FALSE)
  hf_path <- csvs[[1]]
}

df <- read.csv(hf_path)

target_col <- if ("DEATH_EVENT" %in% names(df)) {
  "DEATH_EVENT"
} else {
  cand <- names(df)[vapply(df, function(col) {
    v <- suppressWarnings(as.numeric(col))
    if (all(is.na(v))) return(FALSE)
    u <- unique(v[is.finite(v)])
    length(u) <= 2 && all(sort(u) %in% c(0, 1))
  }, logical(1))]
  if (!length(cand)) stop("Could not infer a binary target column.", call. = FALSE)
  cand[[1]]
}

y_all <- matrix(as.integer(df[[target_col]]), ncol = 1)
x_df  <- df[, setdiff(names(df), target_col), drop = FALSE]
x_all <- as.matrix(x_df)
storage.mode(x_all) <- "double"

n <- nrow(x_all)
idx <- sample.int(n)

n_train <- floor(0.70 * n)
n_valid <- floor(0.15 * n)

i_tr <- idx[1:n_train]
i_va <- idx[(n_train + 1):(n_train + n_valid)]
i_te <- idx[(n_train + n_valid + 1):n]

x_train <- x_all[i_tr, , drop = FALSE]
y_train <- y_all[i_tr, , drop = FALSE]

x_valid <- x_all[i_va, , drop = FALSE]
y_valid <- y_all[i_va, , drop = FALSE]

x_test  <- x_all[i_te, , drop = FALSE]
y_test  <- y_all[i_te, , drop = FALSE]

# Scale train-only (no leakage)
x_train_s <- scale(x_train)
ctr <- attr(x_train_s, "scaled:center")
scl <- attr(x_train_s, "scaled:scale")
scl[!is.finite(scl) | scl == 0] <- 1

x_valid_s <- sweep(sweep(x_valid, 2, ctr, "-"), 2, scl, "/")
x_test_s  <- sweep(sweep(x_test,  2, ctr, "-"), 2, scl, "/")

mx <- suppressWarnings(max(abs(x_train_s)))
if (!is.finite(mx) || mx == 0) mx <- 1

x_train <- x_train_s / mx
x_valid <- x_valid_s / mx
x_test  <- x_test_s  / mx

cat(sprintf("[split] train=%d valid=%d test=%d\n", nrow(x_train), nrow(x_valid), nrow(x_test)))


```

```{r scenarioA_s1_plots, results="asis", echo=FALSE}
options(DDESONN_OUTPUT_ROOT = if (is_check_env) .vig_tmp_root else out1)
Sys.setenv(DDESONN_ARTIFACTS_ROOT = if (is_check_env) .vig_tmp_root else out1)

res_scenarioA_s1 <- tryCatch(
  ddesonn_run(
    x = x_train,
    y = y_train,
    classification_mode = "binary",
    hidden_sizes = c(64, 32),
    seeds = 1L,
    do_ensemble = FALSE,
    validation = list(x = x_valid, y = y_valid),
    test       = list(x = x_test,  y = y_test),
    training_overrides = list(
      init_method = "he",
      optimizer = "adagrad",
      lr = 0.125,
      lambda = 0.00028,
      activation_functions = list(relu, relu, sigmoid),
      dropout_rates = list(0.10),
      loss_type = "CrossEntropy",
      validation_metrics = TRUE,
      num_epochs = 360,
      final_summary_decimals = 6L,

      per_epoch_plots = list(
        saveEnabled = !is_check_env,
        loss_curve = TRUE,
        probe_plots = TRUE,
        verbose = TRUE
      ),

      # ============================================================
      # SECTION: performance_relevance (Scenario 1)
      # - This key is bridged into plot_controls$performance_relevance
      # ============================================================
      performance_relevance = list(
        saveEnabled = !is_check_env,
        viewAllPlots = FALSE,
        performance_high_mean_plots = TRUE,
        performance_low_mean_plots  = TRUE,
        relevance_high_mean_plots   = TRUE,
        relevance_low_mean_plots    = TRUE,
        verbose = TRUE
      ),

      evaluate_predictions_report_plots = list(
        accuracy_plot = TRUE,
        accuracy_plot_mode = "both",
        plot_roc = TRUE,
        plot_pr  = TRUE,
        show_auprc = TRUE,
        viewAllPlots = FALSE,
        verbose = TRUE
      )
    )
  ),
  error = function(e) {
    cat("\n================ DDESONN ERROR — Scenario 1 | Scenario A =================\n")  
    cat(conditionMessage(e), "\n\n")
    cat("-------------- TRACEBACK -----------------------\n")
    traceback(2)
    cat("================================================\n\n")
    stop(e)
  }
)                                                                                       

if (!is_check_env) include_saved_plots(out1, "Scenario 1 — Saved plots")
```

---

## Scenario 2 — One `plot_controls` umbrella (Single Run: Scenario A)

```{r scenarioA_s2_run, donttest=TRUE}
options(DDESONN_OUTPUT_ROOT = if (is_check_env) .vig_tmp_root else out2)
Sys.setenv(DDESONN_ARTIFACTS_ROOT = if (is_check_env) .vig_tmp_root else out2)

res_scenarioA_s2 <- ddesonn_run(
  x = x_train,
  y = y_train,
  classification_mode = "binary",
  hidden_sizes = c(64, 32),
  seeds = 1L,
  do_ensemble = FALSE,
  validation = list(x = x_valid, y = y_valid),
  test       = list(x = x_test,  y = y_test),
  training_overrides = list(
    init_method = "he",
    optimizer = "adagrad",
    lr = 0.125,
    lambda = 0.00028,
    activation_functions = list(relu, relu, sigmoid),
    dropout_rates = list(0.10),
    loss_type = "CrossEntropy",
    validation_metrics = TRUE,
    num_epochs = 1,
    final_summary_decimals = 6L
  ),
  plot_controls = list(
    per_epoch = list(
      saveEnabled = !is_check_env,
      loss_curve = TRUE,
      probe_plots = TRUE,
      verbose = TRUE
    ),

    # ============================================================
    #  SECTION: performance_relevance (Scenario 2)
    # ============================================================
    performance_relevance = list(                                                       
      saveEnabled = !is_check_env,                                                               
      viewAllPlots = FALSE,                                                             
      performance_high_mean_plots = TRUE,                                                
      performance_low_mean_plots  = TRUE,                                                
      relevance_high_mean_plots   = TRUE,                                                
      relevance_low_mean_plots    = TRUE,                                                
      verbose = TRUE                                                                    
    ),                                                                                  

    evaluate_report = list(
      accuracy_plot = TRUE,
      accuracy_plot_mode = "both",
      plot_roc = TRUE,
      plot_pr  = TRUE,
      show_auprc = TRUE,
      viewAllPlots = FALSE,
      verbose = TRUE
    )
  )
)
```

```{r scenarioA_s2_plots, results="asis", echo=FALSE}
if (!is_check_env) include_saved_plots(out2, "Scenario 2 — Saved plots")
```
