Используйте логику маршрутизации при распределении ресурсов с пакетом simmer (или альтернативой) - PullRequest
0 голосов
/ 12 июня 2018

Есть ли способ использовать (настраиваемый) механизм маршрутизации вместе с пакетом simmer для моделирования дискретных событий?(или альтернативный пакет)


Контекст: я запускаю имитацию событий-бетонов (DES) с R. До сих пор все мои симуляции строятся без использования одного из пакетов R, разработанных для DES.Поскольку мой код становится все больше и больше (и производительность ухудшается), я думаю о переключении на один из пакетов R, предназначенных для DES.

Для некоторых частей моего кода я вижу, как я мог бы переключить его на simmer.Но до сих пор я не мог понять, как использовать логику маршрутизации вместе с диспетчеризацией ресурсов.


Пример: следующий минимальный пример показывает, какая функциональность мне нужна (и не мог понятькак строить с симмером).

Генерировать некоторые данные, events (задания) и resources

set.seed(1)

events <- data.frame(
  id = 1:3L,
  t = sort(trunc(rexp(3) * 100)),
  position = runif(3),
  resource = NA,
  worktime = NA
)

resources <- data.frame(
  id = 1:2L,
  position = c(0.2, 0.8),
  t_free = 0
)

Упрощенная версия логики маршрутизации: рассчитать маршрут на основе положенияevent и resources.(Для примера просто указывает на 1-D пробел между 0 и 1, в реальном примере настроенную версию алгоритма OSRM вместе с историческими данными ..)

waytime <- function(events, resources, i) {
  trunc(abs(events$position[i] - resources$position[resources$id == events$resource[i]]) * 100)
}

Две версии симуляции,sim просто берет первый доступный ресурс, не задумываясь о waytime.sim_nearest вычисляет waytimes для всех свободных ресурсов и отправляет ближайшему.sim_nearest - это то, что мне нужно в моих реальных примерах, и я не знаю, как строить, используя simmer.

sim <- function(events, resources) {
  for (i in 1:nrow(events)) {
    # Default dispatching: Use the first free vehicle
    events$resource[i] <- resources$id[resources$t_free <= events$t[i]][1]
    # Simulate event
    events$worktime[i] <- waytime(events, resources, i)
    resources$t_free[events$resource[i]] <- events$t[i] + events$worktime[i]
  }
  return(list(events = events, resources = resources))
}

sim_use_nearest <- function(events, resources) {
  for (i in 1:nrow(events)) {
    # Dispatching by position: Use the nearest free resource
    ids_free <- resources$id[resources$t_free <= events$t[i]]
    events$resource[i] <- resources$id[which.min(abs(resources$position[ids_free] - events$position[i]))]
    # Simulate event
    events$worktime[i] <- waytime(events, resources, i)
    resources$t_free[events$resource[i]] <- events$t[i] + events$worktime[i]
  }
  return(list(events = events, resources = resources))
}

Имитируйте две альтернативы:

res <- sim(events, resources)
res_use_nearest <- sim_use_nearest(events, resources)

Смотрите различия:

res$events
# id   t  position resource worktime
#  1  14 0.9082078        1       70
#  2  75 0.2016819        2       59
#  3 118 0.8983897        1       69
res$resources
# id position t_free
#  1      0.2    187
#  2      0.8    134
res_use_nearest$events
# id   t  position resource worktime
#  1  14 0.9082078        2       10
#  2  75 0.2016819        1        0
#  3 118 0.8983897        2        9
res_use_nearest$resources
# id position t_free
#  1      0.2     75
#  2      0.8    127

Можно ли получить те же результаты с помощью simmer (или другого пакета R DES)?

Ответы [ 3 ]

0 голосов
/ 15 июня 2018

Подход Сэми в порядке, но я бы выбрал немного другой (обратите внимание, что это не проверено, потому что я не написал необходимую функцию routing_logic):

library(simmer)

env <- simmer()

t <- trajectory() %>%
  seize("available_resources") %>%
  set_attribute(c("res_id", "delay"), routing_logic) %>%
  select(function() paste0("res_", get_attribute(env, "res_id"))) %>%
  seize_selected() %>%
  timeout_from_attribute("delay") %>%
  release_selected() %>%
  release("available_resources")

Обратите внимание, что"available_resources" (это должен быть ресурс с емкостью, равной количеству имеющихся у вас ресурсов), это как токен.После захвата это означает, что есть некоторый доступный ресурс.В противном случае события просто сидят и ждут.

routing_logic() должна быть функцией, которая выбирает "res_id" на основе некоторой политики (например, первой доступной или ближайшей), вычисляет задержку и возвращает оба значения, которыехранятся в виде атрибутов.В этой функции вы можете использовать get_capacity(), чтобы узнать состояние каждого ресурса, не устанавливая t_free.Вы также можете получить атрибут position для этого события, который будет установлен автоматически следующим образом:

set.seed(1)

events <- data.frame(
  t = sort(trunc(rexp(3) * 100)),
  position = runif(3)
)

resources <- data.frame(
  id = 1:2L,
  position = c(0.2, 0.8)
)

env %>% 
  add_dataframe("event_", t, events, mon=2, col_time="t", time="absolute") %>%
  add_resource("available_resources", capacity=nrow(resources))

for (id in resources$id) env %>%
  add_resource(paste0("res_", id), capacity=1, queue_size=0)

Как видите, я непосредственно подключил фрейм данных events к траектории (выresource и worktime больше не нужны: первый будет сохранен как атрибут res_id, а последний будет автоматически отслеживаться с помощью simmer и извлекаться с помощью get_mon_arrivals()).Мы указываем, что t - это столбец времени, а другой, position будет добавляться к каждому событию в качестве атрибута, как я уже говорил.

При такой настройке вам просто нужно переопределить routing_logic() для достижения разных политик и разных результатов.

0 голосов
/ 27 июня 2018

Подход Иньяки очень полезен, поскольку он использует функции новейшей версии симмера.Из интереса я дополнил его пример логикой маршрутизации и - как и ожидалось - результаты были такими же.Спасибо за ваш вклад Iñaki.

library(simmer)

env <- simmer()

t <- trajectory() %>%
  seize("available_resources") %>%
  set_attribute(c("res_id", "delay"), function() {
    # find available resources
    capacities <- numeric(nrow(resources))
    for (i in 1:length(capacities)) {
      capacities[i] <- get_server_count(env, paste0("res_", resources$id[i]))
    }
    available <- ifelse(capacities == 0, T, F)
    index_available <- which(available)
    # calculate the delay for available resources
    event_position <- get_attribute(env, "position")
    delay <- trunc(abs(event_position - resources$position[available])*100)
    # take the nearest available resource. 
    index <- index_available[which.min(delay)]
    return(c(index,min(delay)))
  }) %>%
  select(function() paste0("res_", get_attribute(env, "res_id"))) %>%
  seize_selected() %>%
  timeout_from_attribute("delay") %>%
  release_selected() %>%
  release("available_resources")
# --------------------------------------------------------------------
set.seed(1)

events <- data.frame(
  t = sort(trunc(rexp(3) * 100)),
  position = runif(3)
)

resources <- data.frame(
  id = 1:2L,
  position = c(0.2, 0.8)
)

env %>% 
  add_dataframe("event_", t, events, mon=2, col_time="t", time="absolute") %>%
  add_resource("available_resources", capacity=nrow(resources))
for (id in resources$id) env %>%
  add_resource(paste0("res_", id), capacity=1, queue_size=0)

env %>% run()
# --------------------------------------------------------------------
library(simmer.plot)
print(plot(get_mon_resources(env), metric = "usage", c("available_resources", "res_1", "res_2"), items = "server", steps = TRUE))
0 голосов
/ 13 июня 2018

После этого вы найдете возможное решение для вашего минимального примера с пакетом simmer.

Сначала мы выбрали альтернативу имитации, которая позже используется в set_attribute:

sim_first_available <- T
sim_use_nearest <- F

Создайте данные events и resources, как и раньше.

set.seed(1)

events <- data.frame(
  id = 1:3L,
  t = sort(trunc(rexp(3) * 100)),
  position = runif(3),
  resource = NA,
  worktime = NA
)

resources <- data.frame(
  id = 1:2L,
  position = c(0.2, 0.8),
  t_free = 0
)

Начать simmer с траектории sim.

library(simmer)

sim <- trajectory() %>%

Затем установить t_free в качестве глобального атрибута.При первом поступлении (t = 14) вы можете использовать t_free из данных ресурса для инициализации.При последующих поступлениях используйте get_global, чтобы получить текущий t_free определенного ресурса.

  set_global(paste0("t_free_res_", resources$id), function() {
    if (now(env) == 14) {return(resources$t_free) # Initialize parameters when first event arrives
    } else {
      get_global(env, paste0("t_free_res_", resources$id))
    }}) %>%

Теперь определите атрибуты для этого события:

На основе текущего времени моделирования выберитеevent_position из фрейма данных events.

  set_attribute(c("event_position","my_resource", "timeout"), function() {
    t <- now(env)
    event_position <- events$position[events$t == t]

my_resource выбрано в соотв.к альтернативе, которую вы хотите симулировать.

    t_free <- get_global(env, paste0("t_free_res_", resources$id))
    if (sim_first_available & !sim_use_nearest) {
      my_resource <- resources$id[t_free <= now(env)][1]
    } else if (!sim_first_available & sim_use_nearest){
      ids_free <- resources$id[t_free <= now(env)]
      my_resource <- resources$id[which.min(abs(resources$position[ids_free] - event_position))]
    }

На основе resource_pos рассчитать timeout для этого ресурса и вернуть атрибуты:

resource_pos <- resources$position[resources$id == my_resource]
        timeout <- trunc(abs(event_position - resource_pos)*100)

        return(c(event_position, my_resource, timeout))
      }) %>%

Выберите определенный ресурс и воспользуйтесь им:

  select(resources = function() paste0("res_", get_attribute(env, "my_resource"))) %>%
  seize_selected(amount = 1) %>% 

Теперь перезапишите t_free этого ресурса, добавив timeout к текущему времени моделирования.

  set_global(function() {
    paste0("t_free_res_", get_attribute(env, "my_resource"))
  }, function() {
    return(now(env) + get_attribute(env, "timeout"))
  }) %>%

Установите рассчитанное время ожидания для ресурса и отпустите его снова.

  timeout(function() get_attribute(env, "timeout")) %>% 
  release_selected(amount = 1)

Наконец, сгенерируйте события для траектории sim через определенные промежутки времени в событиях, добавьте ресурсы и запустите симуляцию.

env <- simmer()  %>%
  add_generator("event_", sim, at(events$t), mon = 2) %>%
  add_resource("res_1", capacity = 1) %>%
  add_resource("res_2", capacity = 1)

env %>% run()

print(get_mon_attributes(env))
print(get_mon_arrivals(env))
print(get_mon_resources(env))

Надеюсь, это поможет.

...