def get_mask(self, vehicle_count): """ Gets a (batch_size, n_loc + 1) mask with the feasible actions (0 = depot), depends on already visited and remaining capacity. 0 = feasible, 1 = infeasible Forbids to visit depot twice in a row, unless all nodes have been visited :return: """ if self.visited_.dtype == torch.uint8: visited_loc = self.visited_[:, :, vehicle_count:] else: visited_loc = mask_long2bool(self.visited_, n=self.demand.size(-1)) demands = (self.demand.view(-1, 1).repeat(1, vehicle_count).view( self.ids.shape[0], -1))[self.ids, :] used_cap = (self.used_capacity.view( self.used_capacity.shape[0], 1, vehicle_count).repeat(1, 1, demands.shape[-1] // vehicle_count)) # For demand steps_dim is inserted by indexing with id, for used_capacity insert node dim for broadcasting exceeds_cap = (demands + used_cap > self.VEHICLE_CAPACITY) # Nodes that cannot be visited are already visited or too much demand to be served now mask_loc = visited_loc.to(exceeds_cap.dtype) | exceeds_cap # Cannot visit the depot if just visited and still unserved nodes mask_depot = (self.prev_a == 0) & ( (mask_loc == 0).int().sum(-1) > 0)[:, :, None] return torch.cat((mask_depot, mask_loc), -1)
def get_mask(self): """ Gets a (batch_size, n_loc + 1) mask with the feasible actions (0 = depot), depends on already visited and remaining capacity. 0 = feasible, 1 = infeasible Forbids to visit depot twice in a row, unless all nodes have been visited :return: """ if self.visited_.dtype == torch.uint8: visited_loc = self.visited_[:, :, 1:] else: visited_loc = mask_long2bool(self.visited_, n=self.demand.size(-1)) # For demand steps_dim is inserted by indexing with id, for used_capacity insert node dim for broadcasting exceeds_cap = (self.demand[self.ids, :] + self.used_capacity[:, :, None] > self.VEHICLE_CAPACITY) # eta = (self.coords[:, 1:, :] - self.cur_coord).norm(p=2, dim=-1) # not_started_time = (self.time_window_start[self.ids, 1:] > self.cur_time[:, :, None] + eta[:, None, :]) # closed_time = (self.time_window_finish[self.ids, 1:] < self.cur_time[:, :, None] + eta[:, None, :]) # Nodes that cannot be visited are already visited or too much demand to be served now mask_loc = visited_loc.to(exceeds_cap.dtype) | exceeds_cap # | not_started_time | closed_time # Cannot visit the depot if just visited and still unserved nodes mask_depot = (self.prev_a == 0) & ((mask_loc == 0).int().sum(-1) > 0) return torch.cat((mask_depot[:, :, None], mask_loc), -1)
def get_mask(self): """ Gets a (batch_size, n_loc + 1) mask with the feasible actions (0 = depot), depends on already visited and remaining capacity. 0 = feasible, 1 = infeasible Forbids to visit depot twice in a row, unless all nodes have been visited :return: """ if self.visited_.dtype == torch.uint8: visited_loc = self.visited_[:, :, 1:] else: visited_loc = mask_long2bool(self.visited_, n=self.demand.size(-1)) # Nodes that cannot be visited are already visited or too much demand to be served now mask_loc = ( visited_loc | # For demand steps_dim is inserted by indexing with id, for used_capacity insert node dim for broadcasting (self.demand[self.ids, :] + self.used_capacity[:, :, None] > self.VEHICLE_CAPACITY)) # Cannot visit the depot if just visited and still unserved nodes mask_depot = (self.prev_a == 0) & ((mask_loc == 0).int().sum(-1) > 0) return torch.cat((mask_depot[:, :, None], mask_loc), -1)
def visited(self): if self.visited_.dtype == torch.uint8: return self.visited_ else: return mask_long2bool(self.visited_, n=self.coords.size(-2))
def visited(self): if self.visited_.dtype == torch.bool: return self.visited_ else: return mask_long2bool(self.visited_, n=self.loc.size(-2))
def visited(self): if self.visited_.dtype == torch.uint8: return self.visited_ else: return mask_long2bool(self.visited_, n=self.demand.size(-1))