VRPTW: Как обрабатывать временные окна и провалы для специального узла депо? - PullRequest
0 голосов
/ 28 мая 2018

Я считаю, что чтение всех значений присвоения, полученных из

assignment = routing.SolveWithParameters(search_params)

проблем маршрутизации с временными окнами, довольно сложно.Прежде всего, есть узлы и индексы.Я получаю индексы транспортного средства (маршрута) через

index = routing.Start(vehicle)
indices = [index]
while not routing.IsEnd(index):
    index = assignment.Value(routing.NextVar(index))
    indices.append(index)

, а соответствующие узлы получаются с помощью

nodes = [routing.IndexToNode(x) for x in indices]

Для конкретной задачи маршрутизации с 5 остановками и depot=0 решателемнаходит назначение со следующими индексами и узлами:

vehicle: 0
indices: [0, 1, 6]
nodes:   [0, 1, 0]

vehicle: 1
indices: [5, 4, 3, 2, 7]
nodes:   [0, 4, 3, 2, 0]

Таким образом, существует на три индекса больше, чем узлов, потому что каждое транспортное средство начинается и заканчивается в депо.Я определил стоимость 1 для каждого транзита, и чтение значений стоимости через

cost_dimension = routing.GetDimensionOrDie("cost")
cost_vars = [cost_dimension.CumulVar(x) for x in indices]
costs = [assignment.Value(x) for x in cost_vars]

похоже работает:

vehicle: 0
indices: [0, 1, 6]
nodes:   [0, 1, 0]
costs:   [0, 1, 2]

vehicle: 1
indices: [5, 4, 3, 2, 7]
nodes:   [0, 4, 3, 2, 0]
costs:   [0, 1, 2, 3, 4]

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

def time_function(x,y):
    return 30

evaluator = time_function
slack_max = 40
capacity = 24*60
fix_start_cumul_to_zero = False
name = "time"
routing.AddDimension(evaluator, slack_max, capacity, fix_start_cumul_to_zero, name)
time_dimension = routing.GetDimensionOrDie("time")

time_windows = [(7*60, 8*60),(9*60, 10*60),(9*60, 10*60),
                (9*60, 10*60),(9*60, 10*60)]
for node, time_window in enumerate(time_windows):
    time_dimension.CumulVar(node).SetRange(time_window[0], time_window[1])
    routing.AddToAssignment(time_dimension.SlackVar(node))

Таким образом, каждая поездка занимает 30 минут, автомобили могут бездействовать на остановке в течение 40 минут (slack_max=40), и каждая остановка должна обслуживаться с 9:00 до 10:00.Ограничения диапазона, которые вводятся с помощью time_windows[0], предназначены для определения времени начала каждой поездки утром.Но поскольку депо является первой и последней остановкой каждого маршрута, их также можно интерпретировать как время прибытия вечером.

Итак, вот моя первая трудность с временными окнами: депо появляетсядважды на каждом маршруте, но ограничение диапазона определяется на узлах.Я предполагаю, что модель маршрутизации не рассчитана на два окна для депо?

Позвольте мне продолжить, чтобы перейти ко второй части моего вопроса.Я установил fix_start_cumul_to_zero = False, чтобы маршруты могли начинаться в любое время.Также обратите внимание, что routing.AddToAssignment(time_dimension.SlackVar(node)) должен дать мне доступ к переменным Slack позже.Теперь, когда я проверяю значения времени по индексу с помощью

time_vars = [time_dimension.CumulVar(x) for x in indices]
times.append([assignment.Value(x) for x in time_vars])

, отформатированного с datetime, я получаю разумные результаты:

vehicle: 0
indices: [0, 1, 6]
nodes:   [0, 1, 0]
costs:   [0, 1, 2]
times:   ['7:50:00', '9:00:00', '9:30:00']

vehicle: 1
indices: [5, 4, 3, 2, 7]
nodes:   [0, 4, 3, 2, 0]
costs:   [0, 1, 2, 3, 4]
times:   ['7:50:00', '9:00:00', '9:30:00', '10:00:00', '10:30:00']

Решатель, по-видимому, предпочитает время раннего отъезда.Учитывая максимальный провал в 40 минут, каждое транспортное средство может также начать немного позже, например, в 8 часов утра.

Проблема начинается, когда я пытаюсь прочитать переменные слабины через

slack_vars = [time_dimension.SlackVar(x) for x in indices]
slacks = [assignment.Value(x) for x in slack_vars]

Сбой программыс сообщением:

SystemError: возвратил NULL без установки ошибки

, что говорит о том, что time_dimension не имеет переменной слабины для каждого индекса.Это правильно?Почему бы и нет?

Спасибо за чтение этого чрезмерного поста.Вот два вопроса:

  1. Можно ли определить временные окна прибытия и отправления для депо?
  2. Как правильно прочитать слабые места для временных окон для всех узлов, включаяДепо?

1 Ответ

0 голосов
/ 30 мая 2018

Сначала я отвечу на вопрос 2, поскольку 1 - это предположение из этого ответа ...

2) Во-первых, переменная Slack необходима только в том случае, если у вас есть следующий узел, поэтому для этой переменной нетконечный узел.
в основном if j is next(i) then cumul(j) = cumul(i) + transit(i,j) + slack(i)

Вы должны использовать "index", а не "node_index" для доступа / установки SlackVar.
т.е. есть N node_index (то есть N мест, включая депо) но индекс M = N-1/*depot*/ + num_vehicle*2 /*Start, End for each vehicle*/, т. е. у каждого транспортного средства есть конкретный экземпляр объекта для его начального и конечного узла -> поэтому вам необходимо использовать routing.Start (i), чтобы получить «стартовый» индекс i-го транспортного средства (ed:фактически NodeToIndex (0) дает вам стартовый индекс для транспортного средства 0).

def add_time_window_constraints(routing, data, time_evaluator):
    """Add Global Span constraint"""
    time = "Time"
    horizon = 120
    routing.AddDimension(
        time_evaluator,
        horizon, # allow waiting time
        horizon, # maximum time per vehicle
        True, # start cumul to zero
        time)
    time_dimension = routing.GetDimensionOrDie(time)
    for location_idx, time_window in enumerate(data.time_windows):
        if location_idx == 0:
            continue
        time_dimension.CumulVar(routing.NodeToIndex(location_idx)).SetRange(time_window[0], time_window[1])
        routing.AddToAssignment(time_dimension.SlackVar(routing.NodeToIndex(location_idx)))
    for vehicle_id in xrange(data.num_vehicles):
        routing.AddToAssignment(time_dimension.SlackVar(routing.Start(vehicle_id)))
        # slack not defined for vehicle ends
        #routing.AddToAssignment(time_dimension.SlackVar(self.routing.End(vehicle_id)))

1) /! \ NOT TESTED /! \ Для временных окон отправления я бы использовал:

for vehicle_id in xrange(data.num_vehicles):
    time_dimension.CumulVar(routing.Start(vehicle_id)).SetRange(begin, end)

/! \. При создании cumul_start_to_zero необходимо установить значение False.измерение начинается, если> 0, в противном случае оно не найдет решение, очевидно, /! \

т.е. вы должны установить его для каждого транспортного средства ...

Ваш код едва работает, потому что node_index == index для первых узлов он начинает падать ....

ps: я работаю над исправлением документа и примеров использования index / node_index

Полный пример и отслеживаемая проблема в google / or-tools # 708 .
ps: Спасибо, что обнаружили эту проблему;)

...