1

I have implemented or tools (python) for Capacited Vehicle Routing Problem and it is working. I just have another goal in mind: I want to build a callback function that gives each route an upper limit for visiting various types of nodes in a certain group of nodes given different sets of nodes. I have these set of parameters:

% we have two sets of nodes groups = [0,1]

% group 0 has nodes of type 0,1,2 and group 1 has nodes of type 3 and 4. type of nodes = [[0,1,2], [3,4]]

% I have 9 nodes and each type is : node_type = [0, 0,2,2,1,3,3,3,4] % each node belongs to a group node_group_type = [0,0,0,0,0,1,1,1,1]

% from group 0 I should have max 2 different types and from group 1 I can have max 1 type of nodes. max_visits_per_type_group = [2, 1]

visits_per_type = [0 for _ in range(len(max_visits_per_type))]

so my vehicle can contains 0,2,0,2,3,3 or 2,1,4, or 2,3,1,3,3 but It can not have 2,1,3,4 since from group 1 we have two different types as 3 and 4.

I write the call back function as:

# Define your callback function to be called after each feasible solution is found
def limit_node_visits(from_index, to_index):
    from_node = manager.IndexToNode[from_index]
    to_node = manager.IndexToNode[to_index]
    
    from_node_type_group = node_group_type[from_node]
    to_node_type_group = node_group_type[to_node]
     
    from_node_type = node_type[from_node]
    to_node_typ = node_type[to_node]
   

    if (from_node_type == to_node_type):
        return 0  # No cost for same type nodes

    elif visits_per_type[from_node_type] >= max_visits_per_type[from_node_type]:
        return routing.solver().InfeasibleCost()  # Prevent further exploration of this solution
    else:
        visits_per_type[from_node_type_group] += 1
        return 1  # Increment the visit count for the from_node_type

limit_node_visits_index = routing.RegisterUnaryTransitCallback(limit_node_visits)
routing.AddDimensionWithVehicleCapacity(
                                        limit_node_visits_index,
                                         0,
                                         max_visits_per_type_group,
                                         True,
                                         'node_visit') 

However, the code doesn't work and it crashes. It seems that there is something wrong with max_visits_per_type_group in the routing.AddDimensionWithVehicleCapacity() function. I am not sure if I defined the call_back function properly as well. I will appreciate if you help me to write these two functions properly.

1 Answers1

0

Creating a dimension for each sub_type of each group and then adding a constraint involving the end node of each route. Not sure how this scales though. Also, try changing the first_solution_strategy in case there is no solution found.

    max_sub_types_group_zero = 3  # 0, 1 and 2
    dims, callbacks = [], []
    
    for group_zero_sub_type_id in range(max_sub_types_group_zero):
        def sub_type_callback(from_index, id = group_zero_sub_type_id):
            from_node = manager.IndexToNode(from_index)
            return 1 if data["sub_types"][from_node] == id else 0
        
        callbacks.append(sub_type_callback)
        dimension_name =f"sub_type{group_zero_sub_type_id}"
        
        routing.AddDimension(
            routing.RegisterUnaryTransitCallback(callbacks[-1]),
            0,  # no slack
            len(data["distance_matrix"]) + 1,
            True,  # start cumul to zero
            dimension_name,
        )
        dims.append(routing.GetDimensionOrDie(dimension_name))
    
    solver = routing.solver()
    max_distinct_sub_types_group_zero = 2
    
    for veh_id in range(manager.GetNumberOfVehicles()):
        end_idx = routing.End(veh_id)
        expr = []
        
        for group_zero_sub_type_id in range(max_sub_types_group_zero):
             expr.append(solver.Min(dims[group_zero_sub_type_id].CumulVar(end_idx), 1))
    
        solver.Add(sum(expr) <= max_distinct_sub_types_group_zero)
watchdogs132
  • 185
  • 1
  • 1
  • 5
  • 1
    Thank you for your answer @watchdogs132 . The problem of your answer is that it supports situtations in which the total number of nodes from group 0 is 2 so the routes such as [1,1] or [1,2] are ok since the total number of nodes are 2 . However, the routes such as [1,1,2] will be rejected since the total number of nodes are 3 and in addDimension function we restricted it to 2. In my problem the routes such as [1,1,2] or [1,1,2,2] are acceptable too since we have two types of nodes as 1 and 2 but the routes including nodes such as [0,1,2] are not acceptable since it has 3 type of nodes. – Nazanin Moarref May 07 '23 at 13:48
  • Hi @NazaninMoarref, I've edited the answer assuming you haven't already solved the issue! – watchdogs132 Aug 09 '23 at 11:11
  • 1
    Thank you for your help. No I haven't solved the issue. I will try your answer. – Nazanin Moarref Aug 18 '23 at 16:11