# This script creates a private Python virtualenv, installs TensorFlow & h5py,
# loads the pre-trained Keras model in score.h5, displays its architecture,
# and plots a graphical representation of the network.

# 1. Install and load necessary R packages (Just one time!)

# 1) In a fresh R session, before loading keras/tensorflow:
library(reticulate)

# Tell reticulate to use your existing virtualenv
use_virtualenv("r-reticulate", required = TRUE)

# 2) Install TensorFlow (and h5py just to be safe) into that venv
#    This runs pip inside ~/.virtualenvs/r-reticulate
reticulate::virtualenv_install(
  envname  = "r-reticulate",
  packages = c("tensorflow", "h5py")
)

# 3) Confirm both modules are now visible
py_module_available("pydot")      # should be TRUE
py_module_available("tensorflow") # should now be TRUE

# 4) Now load Keras and plot:
library(keras)
# 3. Load the pre-trained Keras model
model_score <- keras::load_model_hdf5("score.h5")

# 4. Print its summary (architecture and parameter counts)
model_score$summary()

# The summary table shows:

# Layer (type)            Output Shape    Param

# ------------------------------------------------

# dense (Dense)           (None, 32)         672

# - First Dense with 32 units.

# - Params = 32 * input_dim + 32 biases = 672 → input_dim = 20.

# - 'None' means unspecified batch size dimension.

# dropout (Dropout)       (None, 32)           0

# - Dropout has no trainable parameters; it applies stochastic masking to inputs.

# dense_1 (Dense)         (None, 32)       1,056

# - Second Dense: 32 * 32 + 32 = 1,056 parameters.

# dropout_1 (Dropout)     (None, 32)           0

# dense_2 (Dense)         (None, 1)            33

# - Final Dense: 1 * 32 + 1 bias = 33 parameters.

# ------------------------------------------------

# Total params:           1,763

# - Sum of all layer parameters.

# Trainable params:       1,761

# - Parameters updated during training.

# Non-trainable params:     0

# - Parameters not updated (e.g., BatchNorm stats).

# Optimizer params:         2

# - Internal optimizer variables (e.g., iteration counters).

tf <- import("tensorflow")
tf$keras$utils$plot_model(
  model_score,
  to_file     = "model_score_table_architecture.png",
  show_shapes = TRUE
)
message("Model diagram saved as model_score_architecture.png")

# 1. Define layer sizes and names
# 1) model’s input shape: (batch, features)
inp_shape <- model_score$input_shape
# reticulate will return a Python tuple, so coerce to R
inp_shape <- py_to_r(inp_shape)    
input_dim <- inp_shape[[2]]
# 2) extract the Dense-layer widths
dense_layers <- Filter(
  function(l) grepl("Dense", class(l)[1]),
  model_score$layers
)
units <- sapply(dense_layers, function(l) l$get_config()$units)
# 3) assemble
layers <- c(
  Input   = input_dim,
  Hidden1 = units[1],
  Hidden2 = units[2],
  Output  = units[3]
)
print(layers)

model_score$loss   # the name of the loss function

# 1) Print all layer activations:
configs <- lapply(model_score$layers, function(layer) layer$get_config())
# The activation is in the “activation” field of each config:
sapply(configs, `[[`, "activation")



# Draw a 4-layer feed-forward MLP with
#   - Input layer (20 scores)
#   - Hidden layer 1 (32 ReLU units)
#   - Hidden layer 2 (32 ReLU units)
#   - Output layer (1 Sigmoid unit)
# using DiagrammeR and GraphViz.

# 0. Install/load dependencies
if (!requireNamespace("DiagrammeR", quietly=TRUE)) install.packages("DiagrammeR")
if (!requireNamespace("glue",       quietly=TRUE)) install.packages("glue")
library(DiagrammeR)
library(glue)


# 2. Generate node IDs for each layer
node_ids <- lapply(layers, function(n) seq_len(n))
names(node_ids) <- names(layers)
# e.g. node_ids$Input   == 1:20

# 3. Start building the DOT script
dot <- "digraph MLP {\n  graph [rankdir=LR];\n  node [shape=circle, fixedsize=true, width=0.4];\n"

# 4. Define colored clusters for each layer
cluster_colors <- c(Input="lightcoral", Hidden1="lightblue",
                    Hidden2="lightblue",   Output="lightgreen")
for (ln in names(layers)) {
  ids   <- node_ids[[ln]]
  color <- cluster_colors[ln]
  lbl   <- if (ln=="Input") "scores" else ln
  dot <- paste0(dot, glue("
  subgraph cluster_{tolower(ln)} {{
    style=filled; color={color}; 
    node [style=filled, fillcolor=white];
    {paste0(ln, ids, collapse='; ')}; 
    label=\"{lbl}\\n{layers[ln]}\";
  }}\n"))
}

# 5. Add node declarations
for (ln in names(layers)) {
  for (i in node_ids[[ln]]) {
    dot <- paste0(dot, glue("  {ln}{i} [label=\" \"];\n"))
  }
}

# 6. Fully-connect the layers
#    Input -> Hidden1
for (i in node_ids$Input) {
  for (j in node_ids$Hidden1) {
    dot <- paste0(dot, glue("  Input{i} -> Hidden1{j};\n"))
  }
}
#    Hidden1 -> Hidden2
for (i in node_ids$Hidden1) {
  for (j in node_ids$Hidden2) {
    dot <- paste0(dot, glue("  Hidden1{i} -> Hidden2{j};\n"))
  }
}
#    Hidden2 -> Output
for (i in node_ids$Hidden2) {
  for (j in node_ids$Output) {
    dot <- paste0(dot, glue("  Hidden2{i} -> Output{j};\n"))
  }
}

# 7. Close the graph
dot <- paste0(dot, "}\n")

# 8. Render with DiagrammeR
graph <- grViz(dot)

# 9. Display and/or save
print(graph)
