# Pseudo likelihood:
IsingPL <- function(
  x, # Vector or data frame containing data
 graph, thresholds, beta, responses = c(0L,1L))
{
  stopifnot(isSymmetric(graph))
  checkResponses(responses)
  stopifnot(length(responses)==2)
  if (any(diag(graph)!=0))
  {
    diag(graph) <- 0
    warning("Diagonal set to 0")
  }
  N <- nrow(graph)
  
  # If x is vector, turn it into a matrix:
  if (is.vector(x)){
    x <- t(x)
  }
  
  if (is.data.frame(x)){
    x <- as.matrix(x)
  }
  
  stopifnot(is.matrix(x))
  
  # Compute pseudo likelihood:
  PL <- PseudoLikelihood(x, graph, thresholds, beta, responses, logis = TRUE)
  
  
  return(PL)
}

# Optimisation:
EstimateIsingPL <- function(data, responses, beta = 1, ...){
  if (missing(responses)){
    responses <- sort(unique(c(data)))
  }

  if (length(responses) != 2){
    stop("Binary data required")
  }

  if (!all(responses %in% c(data))){
    warning("The data do not contain both response options given in 'responses'; 'responses' should match the coding used in the data.")
  }

  # Optimisation function. The pseudo-likelihood depends on the parameters
  # only through beta * graph and beta * thresholds, so optimizing at the
  # user-supplied beta returns parameters that, combined with that beta,
  # reproduce the data-generating measure (params_beta = params_1 / beta):
  optimFun <- function(par, data){
    Ni <- ncol(data)
    stopifnot(length(par) == (Ni*(Ni-1)/2)+Ni)
    graph <- matrix(0,Ni, Ni)
    graph[upper.tri(graph)] <- par[1:(Ni*(Ni-1)/2)]
    graph[lower.tri(graph)] <- t(graph)[lower.tri(graph)]
    thresholds <- par[(Ni*(Ni-1)/2+1):length(par)]
    -2*IsingPL(data, graph, thresholds, beta, responses = responses)
  }
  
  # Run optimizer:
  Ni <- ncol(data)
#   optimRes <- optim(rep(0,(Ni*(Ni-1)/2)+Ni), optimFun, data = Data, ...)
  optimRes <- nlm(optimFun, rep(0,(Ni*(Ni-1)/2)+Ni), data = data, ...)
  
  # Cunstruct graph and thresholds:
  par <- optimRes$estimate
  graph <- matrix(0,Ni, Ni)
  graph[upper.tri(graph)] <- par[1:(Ni*(Ni-1)/2)]
  graph[lower.tri(graph)] <- t(graph)[lower.tri(graph)]
  thresholds <- par[(Ni*(Ni-1)/2+1):length(par)]
  
  return(list(
    graph = graph,
    thresholds = thresholds,
    results = optimRes))
}