R/ssim.R

Defines functions print.ssim_matrix ssim_to_matrix create_ssim

Documented in create_ssim print.ssim_matrix ssim_to_matrix

#' Create SSIM (Structural Self-Interaction Matrix)
#'
#' Creates an empty SSIM template or converts existing data to SSIM format.
#' SSIM uses V/A/X/O notation to describe pairwise relationships between elements.
#'
#' @param n Number of elements, or a character vector of element names.
#' @param labels Optional character vector of element labels. If \code{n} is
#'   a character vector, this parameter is ignored.
#'
#' @return A character matrix of dimension n x n with:
#'   \itemize{
#'     \item Upper triangle: empty strings (to be filled with V/A/X/O)
#'     \item Diagonal: "X" (self-relation)
#'     \item Lower triangle: "-" (mirror of upper, not used directly)
#'   }
#'
#' @details
#' SSIM (Structural Self-Interaction Matrix) is the standard input format for
#' ISM analysis. For each pair of elements (i, j) where i < j, the relationship
#' is coded as:
#'
#' \describe{
#'   \item{V}{Element i influences element j (i -> j)}
#'   \item{A}{Element j influences element i (j -> i)}
#'   \item{X}{Both elements influence each other (i <-> j)}
#'   \item{O}{No relationship between elements}
#' }
#'
#' @seealso
#' \code{\link{ssim_to_matrix}} for converting SSIM to adjacency matrix,
#' \code{\link{create_relation_matrix}} for direct matrix creation.
#'
#' @export
#' @examples
#' # Create empty 4x4 SSIM
#' ssim <- create_ssim(4)
#' print(ssim)
#'
#' # Create with labels
#' ssim <- create_ssim(c("Budget", "Resources", "Quality", "Success"))
#' print(ssim)
#'
#' # Fill in relationships
#' ssim["Budget", "Resources"] <- "V"
#' ssim["Budget", "Quality"] <- "V"
#' ssim["Resources", "Quality"] <- "V"
#' ssim["Quality", "Success"] <- "V"
#' print(ssim)
create_ssim <- function(n, labels = NULL) {
  # Handle character vector input
 if (is.character(n)) {
    labels <- n
    n <- length(labels)
  }

  if (!is.numeric(n) || length(n) != 1 || n < 2) {
    stop("n must be a single integer >= 2 or a character vector of labels",
         call. = FALSE)
  }

  n <- as.integer(n)

  # Generate labels if not provided
  if (is.null(labels)) {
    labels <- paste0("E", seq_len(n))
  }

  if (length(labels) != n) {
    stop("Length of labels must equal n", call. = FALSE)
  }

  # Create empty SSIM matrix
  ssim <- matrix("", nrow = n, ncol = n)
  rownames(ssim) <- colnames(ssim) <- labels

  # Set diagonal to X (self-relation)
  diag(ssim) <- "X"

  # Set lower triangle to "-" (not used directly)
  ssim[lower.tri(ssim)] <- "-"

  class(ssim) <- c("ssim_matrix", "matrix")
  return(ssim)
}

#' Convert SSIM to Adjacency Matrix
#'
#' Converts a Structural Self-Interaction Matrix (SSIM) with V/A/X/O notation
#' to a binary adjacency matrix suitable for ISM analysis.
#'
#' @param ssim A square character matrix with V/A/X/O values in the upper triangle.
#'   Can be created using \code{\link{create_ssim}}.
#' @param validate Logical. If \code{TRUE} (default), validates that all upper
#'   triangle entries are valid (V/A/X/O).
#'
#' @return A square numeric adjacency matrix where:
#'   \itemize{
#'     \item 1 at position (i,j) indicates element i influences element j
#'     \item 0 indicates no direct influence
#'   }
#'
#' @details
#' The conversion rules are:
#' \describe{
#'   \item{V at (i,j)}{Sets adj[i,j] = 1 (i influences j)}
#'   \item{A at (i,j)}{Sets adj[j,i] = 1 (j influences i)}
#'   \item{X at (i,j)}{Sets adj[i,j] = 1 AND adj[j,i] = 1 (mutual influence)}
#'   \item{O at (i,j)}{No edges added}
#' }
#'
#' @seealso
#' \code{\link{create_ssim}} for creating SSIM templates,
#' \code{\link{compute_reachability}} for the next step in ISM analysis.
#'
#' @export
#' @examples
#' # Create and fill SSIM
#' ssim <- create_ssim(c("Budget", "Resources", "Quality", "Success"))
#' ssim["Budget", "Resources"] <- "V"
#' ssim["Budget", "Quality"] <- "V"
#' ssim["Resources", "Quality"] <- "V"
#' ssim["Quality", "Success"] <- "V"
#'
#' # Convert to adjacency matrix
#' adj <- ssim_to_matrix(ssim)
#' print(adj)
#'
#' # Continue with ISM analysis
#' reach <- compute_reachability(adj)
#' levels <- level_partitioning(reach)
ssim_to_matrix <- function(ssim, validate = TRUE) {
  # Input validation
  if (!is.matrix(ssim)) {
    stop("Input must be a matrix", call. = FALSE)
  }
  if (nrow(ssim) != ncol(ssim)) {
    stop("SSIM must be a square matrix", call. = FALSE)
  }

  n <- nrow(ssim)
  labels <- rownames(ssim)

  # Convert to uppercase for comparison
  ssim_upper <- toupper(ssim)

  # Validate entries if requested
  if (validate) {
    upper_vals <- ssim_upper[upper.tri(ssim_upper)]
    valid_codes <- c("V", "A", "X", "O", "")

    invalid <- upper_vals[!upper_vals %in% valid_codes]
    if (length(invalid) > 0) {
      stop("Invalid SSIM codes found: ", paste(unique(invalid), collapse = ", "),
           "\nValid codes are: V, A, X, O",
           call. = FALSE)
    }
  }

  # Create adjacency matrix
  adj <- matrix(0, nrow = n, ncol = n)
  if (!is.null(labels)) {
    rownames(adj) <- colnames(adj) <- labels
  }

  # Process upper triangle
  for (i in seq_len(n - 1)) {
    for (j in (i + 1):n) {
      code <- ssim_upper[i, j]

      if (code == "V") {
        # i influences j
        adj[i, j] <- 1
      } else if (code == "A") {
        # j influences i
        adj[j, i] <- 1
      } else if (code == "X") {
        # Mutual influence
        adj[i, j] <- 1
        adj[j, i] <- 1
      }
      # O or empty: no edges
    }
  }

  return(adj)
}

#' Print SSIM Matrix
#'
#' @param x An object of class \code{ssim_matrix}
#' @param ... Additional arguments passed to print.default
#'
#' @return Invisibly returns the input object
#'
#' @export
#' @method print ssim_matrix
print.ssim_matrix <- function(x, ...) {
  cat("Structural Self-Interaction Matrix (SSIM)\n")
  cat("=========================================\n")
  cat("Codes: V = i->j, A = j->i, X = i<->j, O = no relation\n\n")

  # Print as regular matrix
  class(x) <- "matrix"
  print(x, quote = FALSE, ...)

  invisible(x)
}

Try the ISMtools package in your browser

Any scripts or data that you put into this service are public.

ISMtools documentation built on March 13, 2026, 1:06 a.m.