scripts/TOP_experiment.R

library(dz)
library(tidyverse)
pbapply::pboptions(use_lb = T)

inst <- test_instances$p7_chao

starting_routes <- function(inst, zones, L) {
  # For testing purposes:
  # inst = test_instances$p7_chao; L = 100; k = 3; variances = generate_variances(inst = inst); info = generate_information(inst, r = 20);p_inst <- prepare_instance(inst, variances, info); rb_clust <- rb_clustering(p_inst, L, k, num_routes = 100, info); zones <- rb_clust$zones

  # Join zones on instance
  all_points <- inst$points |>
    dplyr::left_join(
      tibble::tibble(id = zones, zone = 1:length(zones)) |>
        tidyr::unnest(cols = id) |>
        dplyr::filter(id != 1),
      by = c("id")
    )

  all_points$zone[1] <- 0

  # clustering_result <- clustering(inst = test_instances$p7_chao, k = 3, L = 100, eps = 0, cluster_method = "local_search", variances = generate_variances(inst), alpha = 0, info <- generate_information(inst, r = 100))

  same_zone_edges <- inst$edges |>
    dplyr::left_join(
      all_points |> dplyr::select(id, zone),
      by = c("ind1" = "id")
    ) |>
    dplyr::left_join(
      all_points |> dplyr::select(id, zone),
      by = c("ind2" = "id")
    ) |>
    dplyr::filter((zone.x == zone.y) | (zone.x == 0) | (zone.y == 0)) |>
    dplyr::mutate(zone = ifelse(zone.x == 0, zone.y, zone.x)) |>
    dplyr::select(-c(zone.x,zone.y))

  # Function for calculating the distance of the shortest (DL) path between 2 points.
  dist <- function(id1, id2, g){
    # Find vertices that make up the path
    if (id1 == id2) return(0)
    short_vert <- as.vector(igraph::shortest_paths(graph = g, from = id1, to = id2, output = "vpath")$vpath[[1]])
    # Calculate total distance between them
    route_length <- 0
    dist_matrix <- igraph::distances(g)
    for (node in 1:(length(short_vert)-1)){
      temp <- dist_matrix[short_vert[node], short_vert[node+1]]
      route_length <- route_length + temp
    }
    return(route_length)
  }

  # Dist function that returns only the points in the path
  dist2 <- function(id1, id2, g){
    # Find vertices that make up the path
    if (id1 == id2) return(0)
    short_vert <- as.vector(igraph::shortest_paths(graph = g, from = id1, to = id2, output = "vpath")$vpath[[1]])
    return(short_vert)
  }

  ### Function for route length
  route_length <- function(route, g) {
    distance_temp <- vector(length = length(route)-1)
    for (placement in (1):(length(route)-1)) {
      # print(placement)
      distance_temp[placement] <- dist(route[placement], route[placement + 1], g = g)
    }
    return(sum(distance_temp))
  }

  ### Function for route score
  # Use placement of id_next instead of the node id
  route_score <- function(route, id_next_placement) {
    # route <- unique(route)
    score_temp_realized <- vector(length = id_next_placement)
    score_temp_expected <- vector(length = (length(route) - (id_next_placement)))
    for (placement in (1):(length(score_temp_realized)-1)) {
      score_temp_realized[placement] <- map$score[placement]
    }
    for (placement in (1):(length(score_temp_expected)-1)) {
      score_temp_expected[placement] <- map$score[placement]
    }
    return(sum(score_temp_realized, na.rm = T) + sum(score_temp_expected, na.rm = T))
  }

  # solve routing for each zone to get initial route
  solve_routing <- function(obj = 'SDR', L = 100, zone_id = 1){
    # obj = 'SDR'; L = 100; zone_id = 1
    L_remaining <- L

    # Join zones on instance
    all_points <- inst$points |>
      dplyr::left_join(
        tibble::tibble(id = zones, zone = 1:length(zones)) |>
          tidyr::unnest(cols = id) |>
          dplyr::filter(id != 1),
        by = c("id")
      )

    all_points$zone[1] <- 0

    map = all_points |>
      dplyr::filter((id == 1) | (zone == zone_id))

    lookup <- map |> dplyr::mutate(local_id = dplyr::row_number()) |> dplyr::select(local_id, id)

    # Compute edges in delaunay triangulation
    inst$edges <- (deldir::deldir(map$x, map$y))$delsgs

    # construct the igraph object
    inst$edges$dist <- sqrt((inst$edges$x1 - inst$edges$x2)^2 + (inst$edges$y1 - inst$edges$y2)^2) # We assume euclidean distance

    # TESTING
    # edge_ids <- c(inst$edges[,1], inst$edges[,2])
    # node_ids <- lookup$local_id
    #
    # unique(edge_ids)[!unique(edge_ids) %in% node_ids]

    inst$g <- igraph::graph.data.frame(
      inst$edges |> dplyr::select(ind1, ind2, weight = dist),
      directed = FALSE
      ,vertices = lookup$local_id
    )

    # calculate distance matrix (based on shortest path)
    inst$dst <- igraph::distances(inst$g, algorithm = "dijkstra")

    delsgs <- inst$edges

    delsgs$dist <- sqrt((delsgs$x1 - delsgs$x2)^2 + (delsgs$y1 - delsgs$y2)^2)

    # adapt to correct ids (by converting to local ids)
    # lookup <- map |> dplyr::mutate(local_id = dplyr::row_number()) |> dplyr::select(local_id, id)
    map <- map |> dplyr::mutate(local_id = dplyr::row_number(), .before = everything())
    # delsgs <- delsgs |>
    #   dplyr::inner_join(lookup, by = c("ind1" = "id")) |>
    #   dplyr::select(-ind1, ind1 = local_id) |>
    #   dplyr::inner_join(lookup, by = c("ind2" = "id")) |>
    #   dplyr::select(-ind2, ind2 = local_id)

    # create igraph object with local ids
    g <- igraph::graph.data.frame(delsgs |> dplyr::select(ind1, ind2, weight = dist), directed = FALSE, vertices = map |> dplyr::select(local_id, score)
    )

    candidates <- map$local_id
    route = integer()
    route <- append(route, 1)
    last_in_current <- route[length(route)]
    route <- append(route, 1)
    s_total <- 0
    while (L_remaining > 0) {
      # if (tail(route, 2) == c(11,1)) stop()
      if (obj == 'SDR'){
        d <- vector(length = length(map$id))
        s <- vector(length = length(map$id))
        s_path <- vector()
        SDR <- vector(length = length(map$id))
        for (i in 1:length(candidates)) {
          route_temp <- route
          route_temp <- append(route_temp, candidates[i], after = length(route_temp)-1)
          # d[i] <- dist(route[length(route)], candidates[i], g = g) +
          #   dist(candidates[i], route[length(route)-1], g = g) -
          #   as.vector(dist(route[length(route_temp)-2], route_temp[1], g = g))
          #
          d[i] <- dist(route[length(route)-1], candidates[i], g = g) +
            dist(candidates[i], 1, g = g) -
            as.vector(dist(route[length(route_temp)-2], 1, g = g))
          # Make vector of nodes in path to and from candidate, to base score of
          sp1_nodes <- dist2(route[length(route)-1], candidates[i], g = g)
          sp2_nodes <- dist2(candidates[i], 1, g = g)
          s_path <- c(sp1_nodes, sp2_nodes[2:(length(sp2_nodes))])
          # s[i] <- sum(map$score[unique(s_path)]) # consider score to New_last and back to base
          s[i] <- sum(map$score[unique(sp1_nodes)]) # consider only score to New_last, disregard path to base
          # s[i] <- map[candidates[i],]$score
          SDR[i] <- s[i]/d[i]
          SDR[1] <- 0
          SDR[is.na(SDR)] <- 0
          # SDR[!is.finite(SDR)] <- 0
          if ((d[i] <= 0) & (s[i] > 0)) {SDR[i] <- Inf}
        }

        New_last <- which.max(SDR)
        if (New_last == 1) {
          all_short_path_return <- dist2(route[(length(route)-1)], 1, g = g)
          route <- append(route, all_short_path_return[2:(length(all_short_path_return)-1)], after = length(route)-1)
          ### Remove duplicate e.g. 1 56 ... 30 1 30 1
          # for (i in 1:(length(route)-3)) {
          #   if (route[i] == route[i+2] & route[i+1] == route[i+3]) {
          #     route <- route[-i]; route <- route[-i]
          #   }
          # }
          i = 1
          while (i %in% (1:(length(route)-3))) {
            if (is.na(route[i+3]) | is.na(route[i+2])) {
              break
            }
            if (route[i] == route[i+2] & route[i+1] == route[i+3]) {
              route <- route[-i]; route <- route[-i]
              i = i - 2
            }
            i = i + 1
          }
          route_global <- vector(length = length(route))
          for (i in 1:length(route)){
            route_global[i] <- lookup$id[route[i]]
          }
          L_remaining <- L - route_length(route = route, g = g)
          # Function to plot path using information in route object
          output <- list("route" = route_global, "L_remaining" = L_remaining, "s_total" = s_total, "delsgs" = delsgs, "lookup" = lookup)
          print(route)
          return(output)
        }
        sp1_nodes_g <- dist2(route[length(route)-1], New_last, g = g)
        sp2_nodes_g <- dist2(New_last, 1, g = g)
        s_path_g <- c(sp1_nodes_g, sp2_nodes_g[2:(length(sp2_nodes_g))])
        # s_total <- sum(map$score[unique(s_path_g)]) + s_total
        # map$score[unique(s_path_g)] <- 0

        # Moved to below next while loop!
        # all_short_path <- dist2(route[length(route)-1], New_last, g = g)
        # for (node in (all_short_path[2:length(all_short_path)])) {
        #   s_total <- s_total + map$score[node]
        #   map$score[node] <- 0
        # }
      }
      if (obj == 'random'){
        New_last <- sample(2:101, size = 1)
        all_short_path <- dist2(route[length(route)-1], New_last, g = g)
        s_total <- s_total + map[New_last,]$score
        map[New_last,]$score <- 0
        # print(New_last)
      }
      while ((dist(last_in_current, New_last, g = g) + dist(New_last, 1, g = g) - dist(last_in_current,  1, g = g)) > L_remaining){ # if there is not enough range to visit new last, then check the next one
        SDR[New_last] <- 0
        New_last <- which.max(SDR)
        print(New_last)
        print(SDR[New_last])

        if (SDR[New_last] == 0) {# No SDR candidates can be reached, add route back to base
          all_short_path_return <- dist2(route[(length(route)-1)], 1, g = g)
          route <- append(route, all_short_path_return[2:(length(all_short_path_return)-1)], after = length(route)-1)
          ### Remove duplicate e.g. 1 56 ... 30 1 30 1
          # for (i in 1:(length(route)-3)) {
          #   if (route[i] == route[i+2] & route[i+1] == route[i+3]) {
          #     route <- route[-i]; route <- route[-i]
          #   }
          # }
          i = 1
          while (i %in% (1:(length(route)-3))) {
            if (is.na(route[i+3]) | is.na(route[i+2])) {
              break
            }
            if (route[i] == route[i+2] & route[i+1] == route[i+3]) {
              route <- route[-i]; route <- route[-i]
              i = i - 2
            }
            i = i + 1
          }
          route_global <- vector(length = length(route))
          for (i in 1:length(route)){
            route_global[i] <- lookup$id[route[i]]
          }
          L_remaining <- L - route_length(route = route, g = g)
          # Function to plot path using information in route object
          output <- list("route" = route_global, "L_remaining" = L_remaining, "s_total" = s_total, "delsgs" = delsgs, "lookup" = lookup)
          print(route)
          return(output)
        }
      }
      all_short_path <- dist2(route[length(route)-1], New_last, g = g)
      for (node in (all_short_path[2:length(all_short_path)])) {
        s_total <- s_total + map$score[node]
        map$score[node] <- 0
      }

      route <- append(route, all_short_path[2:length(all_short_path)], after = length(route)-1)
      last_in_current <- route[length(route)-1]
      print(route)
      L_remaining <- L - route_length(route = route, g = g)
    }
  }

  # Use the result from solve_routing and update by adding until no more range in the same way as solve_routing

  improve_routing <- function(L_remaining, L, route, zone_id){
    # L_remaining = r$L_remaining; route = r$route; zone_id = 1
    # route <- lookup$local_id[match(lookup$local_id, route)]

    # L_remaining <- L_remaining/2

    ##### OLD
    # map = all_points |>
    #   dplyr::filter((id == 1) | (zone == zone_id))
    #
    # delsgs <- same_zone_edges |>
    #   dplyr::filter(zone == zone_id) |>
    #   tibble::as_tibble()
    #
    # delsgs$dist <- sqrt((delsgs$x1 - delsgs$x2)^2 + (delsgs$y1 - delsgs$y2)^2)
    #
    # # adapt to correct ids (by converting to local ids)
    # lookup <- map |> dplyr::mutate(local_id = dplyr::row_number()) |> dplyr::select(local_id, id)
    # map <- map |> dplyr::mutate(local_id = dplyr::row_number(), .before = everything())
    # delsgs <- delsgs |>
    #   dplyr::inner_join(lookup, by = c("ind1" = "id")) |>
    #   dplyr::select(-ind1, ind1 = local_id) |>
    #   dplyr::inner_join(lookup, by = c("ind2" = "id")) |>
    #   dplyr::select(-ind2, ind2 = local_id)
    #
    # # create igraph object with local ids
    # g <- igraph::graph.data.frame(delsgs |>  dplyr::select(ind1, ind2, weight = dist), directed = FALSE, vertices = map |> dplyr::select(local_id, score))

    ##### NEW
    # Join zones on instance
    all_points <- inst$points |>
      dplyr::left_join(
        tibble::tibble(id = zones, zone = 1:length(zones)) |>
          tidyr::unnest(cols = id) |>
          dplyr::filter(id != 1),
        by = c("id")
      )

    all_points$zone[1] <- 0

    map = all_points |>
      dplyr::filter((id == 1) | (zone == zone_id))

    lookup <- map |> dplyr::mutate(local_id = dplyr::row_number()) |> dplyr::select(local_id, id)

    # Compute edges in delaunay triangulation
    inst$edges <- (deldir::deldir(map$x, map$y))$delsgs

    # construct the igraph object
    inst$edges$dist <- sqrt((inst$edges$x1 - inst$edges$x2)^2 + (inst$edges$y1 - inst$edges$y2)^2) # We assume euclidean distance

    # TESTING
    # edge_ids <- c(inst$edges[,1], inst$edges[,2])
    # node_ids <- lookup$local_id
    #
    # unique(edge_ids)[!unique(edge_ids) %in% node_ids]

    inst$g <- igraph::graph.data.frame(
      inst$edges |> dplyr::select(ind1, ind2, weight = dist),
      directed = FALSE
      ,vertices = lookup$local_id
    )

    # calculate distance matrix (based on shortest path)
    inst$dst <- igraph::distances(inst$g, algorithm = "dijkstra")

    delsgs <- inst$edges

    delsgs$dist <- sqrt((delsgs$x1 - delsgs$x2)^2 + (delsgs$y1 - delsgs$y2)^2)

    # adapt to correct ids (by converting to local ids)
    # lookup <- map |> dplyr::mutate(local_id = dplyr::row_number()) |> dplyr::select(local_id, id)
    map <- map |> dplyr::mutate(local_id = dplyr::row_number(), .before = everything())
    # delsgs <- delsgs |>
    #   dplyr::inner_join(lookup, by = c("ind1" = "id")) |>
    #   dplyr::select(-ind1, ind1 = local_id) |>
    #   dplyr::inner_join(lookup, by = c("ind2" = "id")) |>
    #   dplyr::select(-ind2, ind2 = local_id)

    # create igraph object with local ids
    g <- igraph::graph.data.frame(delsgs |> dplyr::select(ind1, ind2, weight = dist), directed = FALSE, vertices = map |> dplyr::select(local_id, score)
    )

    route <- sapply(route, function(x) lookup$local_id[lookup$id == x])

    # Fix instant repeat of nodes in route
    j = 1
    while (j %in% (1:(length(route)-1))) {
      if (is.na(route[j+1])) {
        break
      }
      if (route[j] == route[j+1]) {
        route <- route[-j]
        j = j - 1
      }
      j = j + 1
    }

    map$score[route] <- 0

    candidates <- map$local_id[!(map$local_id) %in% route]
    # last_in_current <- route[length(route)]
    s_total <- 0

    d <- list()
    s <- list()
    SDR <- list()
    rl <- length(route)-1
    while (L_remaining > 0) {
      for (n in 1:rl) {
        d[[n]] <- vector(length = length(candidates))
        s[[n]] <- vector(length = length(candidates))
        SDR[[n]] <- vector(length = length(candidates))
        for (i in (1:(length(candidates)))) {
          route_temp <- route
          route_temp <- append(route_temp, candidates[i], after = n)
          d[[n]][i] <- dist(route_temp[n], candidates[i], g = g) +
            dist(candidates[i], route_temp[n+2], g = g) -
            dist(route_temp[n], route_temp[n+2], g = g)
          # Uses SDR for all nodes is the shortest paths
          nodes_visited <- unique(c(dist2(route[n], candidates[i], g = g), dist2(candidates[i], route[n+1], g = g)))
          s[[n]][i] <- sum(map$score[nodes_visited])
          SDR[[n]][i] <- (s[[n]][i])/(d[[n]][i])
          SDR[[n]][i][!is.finite(SDR[[n]][i])] <- 0
          if (d[[n]][i] > L_remaining){
            SDR[[n]][i] <- 0
          }
        }
      }
      New_node <- vector(length = rl)
      value <- vector(length = rl)
      # New_node[n] <- which.max(SDR[[n]])
      for (nr in 1:rl) {
        New_node[nr] <- which.max(SDR[[nr]])
        value[nr] <- max(SDR[[nr]])
      }
      if (max(value) == 0) {
        route_global <- vector(length = length(route))
        for (i in 1:length(route)){
          route_global[i] <- lookup$id[route[i]]
        }
        L_remaining <- L - route_length(route = route, g = g)
        output <- list("route" = route_global, "L_remaining" = L_remaining, "delsgs" = delsgs, "lookup" = lookup)
        return(output)
      }
      # Værdien i New_node er candidate id_local med højest SDR og indgangen er hvor i ruten det tilføjes efter
      New_node_placement <- which.max(value)
      ### Insert New_node in the right place
      # Includes shortest path to and from new node, not just the highest SDR node itself
      short_path_to <- dist2(route[New_node_placement], candidates[New_node[New_node_placement]], g = g)
      short_path_back <- dist2(candidates[New_node[New_node_placement]], route[(New_node_placement+1)], g = g)
      route <- append(route, short_path_to[2:length(short_path_to)], after = New_node_placement)
      route <- append(route, short_path_back[2:(length(short_path_back)-1)], after = (New_node_placement + length(short_path_to) - 1))
      # route <- append(route, New_node[New_node_placement], after = New_node_placement)
      # Update L_remaining
      L_remaining <- L - route_length(route, g = g)
      # Update score
      # map$score[New_node[New_node_placement]] <- 0
      map$score[unique(c(short_path_to, short_path_back))] <- 0
      ### Remove duplicate e.g. 1 56 ... 30 1 30 1
      # for (i in 1:(length(route)-4)) {
      #   if ((route[i] == route[i+2]) && (route[i+1] == route[i+3])) {
      #     route <- route[-i]; route <- route[-i]
      #   }
      # }
      i = 1
      while (i %in% (1:(length(route)-3))) {
        if (is.na(route[i+3])) {
          break
        }
        if ((route[i] == route[i+2]) && (route[i+1] == route[i+3])) {
          route <- route[-i]; route <- route[-i]
          i = i - 2
        }
        i = i + 1
      }
    }
    route_global <- vector(length = length(route))
    for (i in 1:length(route)){
      route_global[i] <- lookup$id[route[i]]
    }
    L_remaining <- L - route_length(route = route, g = g)
    output <- list("route" = route_global, "L_remaining" = L_remaining, "delsgs" = delsgs, "lookup" = lookup)
    return(output)
  }

  # Testing
  # r <- solve_routing(zone_id =  1)
  # imr <- improve_routing(L_remaining = r$L_remaining, route = r$route, L = 100, zone_id = 1)

  # we want to create a route for each zone
  routing_results <- tibble::tibble(agent_id = 1:length(zones))

  # calculate the routes
  message("Finding the initial routes")
  already_used <- c()
  improved_routes <- pbapply::pblapply(
    routing_results$agent_id,
    function(zone_id) {
      sr <- solve_routing(obj = "SDR", L = L, zone_id = zone_id)
      ir <- improve_routing(L_remaining = sr$L_remaining,
                            L,
                            sr$route,
                            zone_id)
      already_used <<- append(already_used, ir$route)
      # zones[[zone_id + 1]] <- c(1,zones[[zone_id + 1]][!zones[[zone_id + 1]] %in% already_used])
      try(zones[[zone_id + 1]] <<- c(1,zones[[zone_id + 1]][!zones[[zone_id + 1]] %in% already_used]))
      return(ir)
    }
  )

  # message("Trying to improve initial routes")
  # improved_routes <- pbapply::pblapply(
  #   routing_results$agent_id,
  #   function(zone_id) {improve_routing(
  #     L_remaining = initial_routes[[zone_id]]$L_remaining,
  #     route = initial_routes[[zone_id]]$route,
  #     # L = L - initial_routes[[zone_id]]$L_remaining/2,
  #     L = L,
  #     zone_id = zone_id
  #   )}
  # )

  # return(initial_routes)

  structure(
    list(
      # "initial_routes" = lapply(initial_routes, function(x) x$route),
      "improved_routes" = lapply(improved_routes, function(x) x$route),
      "L_remaining" = lapply(improved_routes, function(x) x$L_remaining),
      "total_score" = lapply(improved_routes, function(x) sum(inst$points$score[unique(x$route)])),
      "zones" = zones,
      "L" = L
    ),
    class = "starting_routes"
  )
}

no_zone_sr <- function(inst, L, k) {
  zones <- list()
  for (i in 1:k) zones[[i]] <- inst$points$id
  sr <- starting_routes(inst, zones, L)
  return(sr)
}

plot_route <- function(routes) {
  route_segments <- tibble::tibble(routes, agent_id = 1:length(routes)) |>
    tidyr::unnest(routes) |>
    dplyr::group_by(agent_id) |>
    dplyr::mutate(id_start = dplyr::lag(routes), id_end = routes) |>
    dplyr::filter(!is.na(id_start)) |>
    dplyr::select(-routes) |>
    dplyr::inner_join(inst$points |> dplyr::select(id, x, y),
                      by = c("id_start" = "id")) |>
    dplyr::inner_join(inst$points |> dplyr::select(id, x, y),
                      by = c("id_end" = "id"), suffix = c("","end"))

  ggplot2::ggplot() +
    ggplot2::geom_text(
      data = inst$points |> dplyr::filter(point_type == "intermediate"),
      ggplot2::aes(x, y, label = id)
    ) +
    ggplot2::geom_point(
      data = inst$points |> dplyr::filter(point_type == "terminal"),
      ggplot2::aes(x, y), color = "red", shape = 17
    ) +
    ggplot2::geom_segment(
      data = route_segments,
      ggplot2::aes(x=x, y=y, xend=xend, yend=yend, linetype = as.character(agent_id))
    ) +
    # ggplot2::ggtitle(paste0("Instance: ", inst$name)) +
    ggplot2::theme_bw() +
    ggplot2::guides(shape = "none", size = "none") +
    ggplot2::labs(x = "x", y = "y", linetype = "Agent id")
}

combined_results <- readRDS("combined_results.rds") |>
  # filter(clustering_method != "heuristic") |>
  bind_rows(
    readRDS("combined_results_relevancy.rds") |> filter(clustering_method == "heuristic relevancy")
  ) |> ungroup()

combined_results <- combined_results |>
  select(clustering_method, k, L, sr_score, p_inst, clust, sr, scenarios, ur_score, candidate_outside) |>
  mutate(clustering_method = factor(clustering_method, levels = c("routing-based", "heuristic", "heuristic relevancy"),
                                    labels = c("RB", "HC", "HR")))

# find no_zone routes for all k,L
params <- combined_results[,c("k", "L")] |> distinct()

num_cores <- parallel::detectCores(logical = F)
cl <- parallel::makeCluster(num_cores)

invisible(parallel::clusterEvalQ(cl, {library(dz)}))
parallel::clusterExport(cl, c('params', 'inst', 'starting_routes', 'no_zone_sr'))

nz_sr <- pbapply::pblapply(1:nrow(params), function(i) {
  k <- params$k[i]; L <- params$L[i]/k
  nz_sr <- no_zone_sr(inst, L, k)
  return(nz_sr)
}, cl = cl
)

saveRDS(nz_sr, "nz_sr.rds")

# TODO: use ur_scenario function and record realized scores for no zone starting routes
nz_sr <- readRDS("nz_sr.rds")

ur_scenario <- function(row_id) {
  # variables
  p_inst <- combined_results$p_inst[[row_id]]
  if (class(combined_results$clust[[row_id]]) == "rb_clustering") {
    zones <- combined_results$clust[[row_id]]$zones
  } else {
    zones <- combined_results$clust[[row_id]]$cl$zones
  }
  L <- combined_results$L[row_id] / length(zones)
  k <- length(zones)
  sr <- combined_results$sr[[row_id]]
  info <- combined_results$p_inst[[row_id]]$info

  # update unexpected and realized score
  p_inst$points <- p_inst$points |>
    mutate(unexpected = purrr::rbernoulli(1, p = p_unexpected))

  for (i in p_inst$points$id) { # we need to consider all nodes
    related_nodes <- which(info[i,] != 0) # find the nodes that are related
    for (j in related_nodes) { # update score
      p_inst$points$expected_score[j] <- p_inst$points$score[j] + p_inst$points$p_unexpected[i] * info[i,j]
      if (p_inst$points$unexpected[i]) {
        p_inst$points$realized_score[j] <- p_inst$points$score[j] + info[i,j]
      }
    }
  }

  p_inst$points$expected_score[1] <- 0; p_inst$points$realized_score[1] <- 0

  # Run update_routes2
  ur <- update_routes2(p_inst, zones, L, k, sr, info)

  # Find realized scores for no zone sr
  nz_sr_id <- params |>
    mutate(r = row_number()) |>
    filter(k == length(zones), L == combined_results$L[row_id]) |>
    pull(r)

  nz_sr_score <- sum(p_inst$points$realized_score[
    unique(do.call(c, nz_sr[[nz_sr_id]]$improved_routes))
  ])

  # construct the realized_score over time
  ur_scores <- lapply(seq_along(ur$routes), function(route_id){
    route_time_n_score <- function(id) {
      sub_route <- ur$routes[[route_id]][1:id]

      score <- sum(p_inst$points$realized_score[unique(sub_route)])
      L_used <- tryCatch(sum(p_inst$dst[embed(sub_route, 2)]), error = function(e) 0)

      tibble(L_used, score, route_id)
    }

    do.call(bind_rows, lapply(seq_along(ur$routes[[route_id]]), route_time_n_score))
  })

  ur_scores_w_L <- do.call(bind_rows, ur_scores) |>
    pivot_wider(id_cols = c(L_used), names_from = "route_id", values_from = "score") |>
    arrange(L_used) |>
    fill(-L_used) |>
    mutate(total_score = rowSums(across(-L_used))) |>
    select(L_used, total_score)

  # return results
  list(
    "ur" = ur,
    "total_realized_score" = ur$total_realized_score,
    "nz_total_realized_score" = nz_sr_score,
    "candidate_outside" = ur$candidate_outside,
    "ur_scores_w_L" = ur_scores_w_L
  )
}

num_cores <- parallel::detectCores(logical = F)
cl <- parallel::makeCluster(num_cores)

parallel::clusterExport(cl, c('combined_results', 'ur_scenario', 'nz_sr', 'params'))
invisible(parallel::clusterEvalQ(cl, {library(dz); library(tidyverse)}))

combined_scenarios <- pbapply::pblapply(1:nrow(combined_results), function(row_id) {
  reps = 1:50
  scenarios <- lapply(reps, function(x) ur_scenario(row_id))
  names(scenarios) <- reps
  return(scenarios)
}, cl = cl)

combined_results$scenarios <- combined_scenarios

message("Clean and augment the combined results dataset...")
# combined_rslt$scenarios[[1]][[1]]

combined_results$ur_score <- lapply(
  combined_results$scenarios,
  function(i) sapply(i, function(j) do.call(sum, j$total_realized_score))
)

combined_results$nz_ur_score <- lapply(
  combined_results$scenarios,
  function(i) sapply(i, function(j) j$nz_total_realized_score)
)

combined_results$candidate_outside <- lapply(
  combined_results$scenarios,
  function(i) sapply(i, function(j) do.call(sum, j$candidate_outside))
)

saveRDS(combined_results, "cr_nz.rds")

cr_nz <- readRDS("cr_nz.rds")

cr_nz <- cr_nz |>
  mutate(mean_ur_score = sapply(ur_score, mean), mean_nz_ur_score = sapply(nz_ur_score, mean)) |>
  select(clustering_method, k, L, mean_ur_score, mean_nz_ur_score) |>
  mutate(`Number of agents` = factor(k))

gg_color_hue <- function(n) {
  hues = seq(15, 375, length = n + 1)
  hcl(h = hues, l = 65, c = 100)[1:n]
}

gg_color_hue(4)

a=.3; cr_nz |>
  filter(clustering_method == "RB") |>
  ggplot(aes(x = L)) +

    geom_point(data = filter(cr_nz, clustering_method == "RB"),
               aes(y = mean_ur_score, color = "RB"), alpha = a) +
    geom_line(data = filter(cr_nz, clustering_method == "RB"),
              aes(y = mean_ur_score, color = "RB"), alpha = a) +

    geom_point(data = filter(cr_nz, clustering_method == "HC"),
               aes(y = mean_ur_score, color = "HC"), alpha = a) +
    geom_line(data = filter(cr_nz, clustering_method == "HC"),
              aes(y = mean_ur_score, color = "HC"), alpha = a) +

    geom_point(data = filter(cr_nz, clustering_method == "HR"),
               aes(y = mean_ur_score, color = "HR"), alpha = a) +
    geom_line(data = filter(cr_nz, clustering_method == "HR"),
              aes(y = mean_ur_score, color = "HR"), alpha = a) +

    geom_point(aes(y = mean_nz_ur_score, color = "TOP")) +
    geom_line(aes(y = mean_nz_ur_score, color = "TOP")) +

    scale_color_manual(values = c("#7CAE00","#00BFC4","#F8766D","#C77CFF")) +

    facet_wrap(~`Number of agents`, labeller = "label_both") +
    theme_bw() + labs(color = "Solution method", x = "Total L across team", y = "Mean total realized score") +
    theme(legend.position = "top")

ggsave("./figures_for_report/TOP_comparison.pdf", width = 8, height = 3.5)

plot_nz <- function(sr, inst) {
  route_segments <- tibble::tibble(routes = sr$improved_routes, agent_id = 1:length(sr$improved_routes)) |>
    tidyr::unnest(routes) |>
    dplyr::group_by(agent_id) |>
    dplyr::mutate(id_start = dplyr::lag(routes), id_end = routes) |>
    dplyr::filter(!is.na(id_start)) |>
    dplyr::select(-routes) |>
    dplyr::inner_join(inst$points |> dplyr::select(id, x, y),
                      by = c("id_start" = "id")) |>
    dplyr::inner_join(inst$points |> dplyr::select(id, x, y),
                      by = c("id_end" = "id"), suffix = c("","end"))

  ggplot2::ggplot() +
    # ggplot2::geom_segment(
    #   data = inst$edges,
    #   ggplot2::aes(x = x1, y = y1, xend = x2, yend = y2),
    #   color = ggplot2::alpha("black", 0.3), linetype = "dashed"
    # ) +
    ggplot2::geom_point(
      data = inst$points |>
        dplyr::filter(point_type == "intermediate"), #|>
        # dplyr::left_join(temp, by = c("id")),
      ggplot2::aes(x, y, size = score), color = "grey"
    ) +
    ggplot2::geom_segment(
      data = route_segments,
      ggplot2::aes(x=x, y=y, xend=xend, yend=yend, color = as.character(agent_id)),
      size = .75
    ) +
    ggplot2::geom_point(
      data = inst$points |> dplyr::filter(point_type == "terminal"),
      ggplot2::aes(x, y), color = "red", shape = 17
    ) +
    # ggplot2::scale_color_manual(values = c("black", scales::hue_pal()(k))) +
    # ggplot2::ggtitle(paste0("Instance: ", inst$name)) +
    ggplot2::theme_bw() +
    ggplot2::guides(
      shape = "none",
      fill = "none",
      # color = "none",
      alpha = "none",
      size = "none"
    ) +
    ggplot2::labs(
      color = "Agent id", x = "x", y = "y"
    ) +
    ggplot2::coord_fixed()
}

i = 33
plot(combined_results$scenarios[[i]]$`1`$ur, inst = test_instances$p7_chao)
ggsave(paste0("./figures_for_report/", i, "_ZTOP.png"), width = 5, height = 5)
plot_nz(nz_sr[[i]], inst = test_instances$p7_chao)
ggsave(paste0("./figures_for_report/", i, "_TOP.png"), width = 5, height = 5)

# statistical test

cr_nz <- readRDS("cr_nz.rds")
stat_data_cluster <- cr_nz |>
  select(clustering_method, k, L, scenarios, nz_ur_score) |>
  unnest(cols = scenarios) |>
  mutate(ur_score = sapply(scenarios, function(x) do.call(sum, x$total_realized_score))) |>
  select(clustering_method, k, L, ur_score)

stat_data_nz <- cr_nz |>
  select(clustering_method, k, L, nz_ur_score) |>
  filter(clustering_method == "RB") |>
  mutate(clustering_method = "TOP") |>
  unnest(cols = nz_ur_score) |>
  rename(ur_score = nz_ur_score)

stat_data <- bind_rows(stat_data_cluster, stat_data_nz) |>
  mutate(clustering_method = factor(clustering_method, levels = c("TOP", "RB", "HC", "HR")))


summary(lm(ur_score ~ clustering_method, data = stat_data |> filter(k == 2)))
summary(lm(ur_score ~ clustering_method, data = stat_data |> filter(k == 3)))
summary(lm(ur_score ~ clustering_method, data = stat_data |> filter(k == 4)))

xtable::xtable(summary(lm(ur_score ~ clustering_method, data = stat_data)))
Rosenkrands/dz documentation built on June 26, 2022, 10:43 p.m.