(self, static, dynamic, decoder_input=None, last_hh=None)
return tour_idx, tour_logp
Parameters
        ----------
        static: Array of size (batch_size, feats, num_cities)
            Defines the elements to consider as static. For the TSP, this could be
            things like the (x, y) coordinates, which won't change
        dynamic: Array of size (batch_size, feats, num_cities)
            Defines the elements to consider as static. For the VRP, this can be
            things like the (load, demand) of each city. If there are no dynamic
            elements, this can be set to None
        decoder_input: Array of size (batch_size, num_feats)
            Defines the outputs for the decoder. Currently, we just use the
            static elements (e.g. (x, y) coordinates), but this can technically
            be other things as well
        last_hh: Array of size (batch_size, num_hidden)
            Defines the last hidden state for the RNN
Parameters
        ----------
        static: Array of size (batch_size, feats, num_cities)
            Defines the elements to consider as static. For the TSP, this could be
            things like the (x, y) coordinates, which won't change
        dynamic: Array of size (batch_size, feats, num_cities)
            Defines the elements to consider as static. For the VRP, this can be
            things like the (load, demand) of each city. If there are no dynamic
            elements, this can be set to None
        decoder_input: Array of size (batch_size, num_feats)
            Defines the outputs for the decoder. Currently, we just use the
            static elements (e.g. (x, y) coordinates), but this can technically
            be other things as well
        last_hh: Array of size (batch_size, num_hidden)
            Defines the last hidden state for the RNN
Parameters ---------- static : Array of size ( batch_size feats num_cities ) Defines the elements to consider as static . For the TSP this could be things like the ( x y ) coordinates which won t change dynamic : Array of size ( batch_size feats num_cities ) Defines the elements to consider as static . For the VRP this can be things like the ( load demand ) of each city . If there are no dynamic elements this can be set to None decoder_input : Array of size ( batch_size num_feats ) Defines the outputs for the decoder . Currently we just use the static elements ( e . g . ( x y ) coordinates ) but this can technically be other things as well last_hh : Array of size ( batch_size num_hidden ) Defines the last hidden state for the RNN
def forward(self, static, dynamic, decoder_input=None, last_hh=None):
        """
        Parameters
        ----------
        static: Array of size (batch_size, feats, num_cities)
            Defines the elements to consider as static. For the TSP, this could be
            things like the (x, y) coordinates, which won't change
        dynamic: Array of size (batch_size, feats, num_cities)
            Defines the elements to consider as static. For the VRP, this can be
            things like the (load, demand) of each city. If there are no dynamic
            elements, this can be set to None
        decoder_input: Array of size (batch_size, num_feats)
            Defines the outputs for the decoder. Currently, we just use the
            static elements (e.g. (x, y) coordinates), but this can technically
            be other things as well
        last_hh: Array of size (batch_size, num_hidden)
            Defines the last hidden state for the RNN
        """

        batch_size, input_size, sequence_size = static.size()

        if decoder_input is None:
            decoder_input = self.x0.expand(batch_size, -1, -1)

        # Always use a mask - if no function is provided, we don't update it
        mask = torch.ones(batch_size, sequence_size, device=device)

        # Structures for holding the output sequences
        tour_idx, tour_logp = [], []
        max_steps = sequence_size if self.mask_fn is None else 1000

        # Static elements only need to be processed once, and can be used across
        # all 'pointing' iterations. When / if the dynamic elements change,
        # their representations will need to get calculated again.
        static_hidden = self.static_encoder(static)
        dynamic_hidden = self.dynamic_encoder(dynamic)

        for _ in range(max_steps):

            if not mask.byte().any():
                break

            # ... but compute a hidden rep for each element added to sequence
            decoder_hidden = self.decoder(decoder_input)

            probs, last_hh = self.pointer(static_hidden,
                                          dynamic_hidden,
                                          decoder_hidden, last_hh)
            probs = F.softmax(probs + mask.log(), dim=1)

            # When training, sample the next step according to its probability.
            # During testing, we can take the greedy approach and choose highest
            if self.training:
                m = torch.distributions.Categorical(probs)

                # Sometimes an issue with Categorical & sampling on GPU; See:
                # https://github.com/pemami4911/neural-combinatorial-rl-pytorch/issues/5
                ptr = m.sample()
                while not torch.gather(mask, 1, ptr.data.unsqueeze(1)).byte().all():
                    ptr = m.sample()
                logp = m.log_prob(ptr)
            else:
                prob, ptr = torch.max(probs, 1)  # Greedy
                logp = prob.log()

            # After visiting a node update the dynamic representation
            if self.update_fn is not None:
                dynamic = self.update_fn(dynamic, ptr.data)
                dynamic_hidden = self.dynamic_encoder(dynamic)

                # Since we compute the VRP in minibatches, some tours may have
                # number of stops. We force the vehicles to remain at the depot 
                # in these cases, and logp := 0
                is_done = dynamic[:, 1].sum(1).eq(0).float()
                logp = logp * (1. - is_done)

            # And update the mask so we don't re-visit if we don't need to
            if self.mask_fn is not None:
                mask = self.mask_fn(mask, dynamic, ptr.data).detach()

            tour_logp.append(logp.unsqueeze(1))
            tour_idx.append(ptr.data.unsqueeze(1))

            decoder_input = torch.gather(static, 2,
                                         ptr.view(-1, 1, 1)
                                         .expand(-1, input_size, 1)).detach()

        tour_idx = torch.cat(tour_idx, dim=1)  # (batch_size, seq_len)
        tour_logp = torch.cat(tour_logp, dim=1)  # (batch_size, seq_len)

        return tour_idx, tour_logp
def forward ( self , static , dynamic , decoder_input = None , last_hh = None ) : batch_size , input_size , sequence_size = static . size ( ) if decoder_input is None : decoder_input = self . x0 . expand ( batch_size , - 1 , - 1 ) # Always use a mask - if no function is provided, we don't update it mask = torch . ones ( batch_size , sequence_size , device = device ) # Structures for holding the output sequences tour_idx , tour_logp = [ ] , [ ] max_steps = sequence_size if self . mask_fn is None else 1000 # Static elements only need to be processed once, and can be used across # all 'pointing' iterations. When / if the dynamic elements change, # their representations will need to get calculated again. static_hidden = self . static_encoder ( static ) dynamic_hidden = self . dynamic_encoder ( dynamic ) for _ in range ( max_steps ) : if not mask . byte ( ) . any ( ) : break # ... but compute a hidden rep for each element added to sequence decoder_hidden = self . decoder ( decoder_input ) probs , last_hh = self . pointer ( static_hidden , dynamic_hidden , decoder_hidden , last_hh ) probs = F . softmax ( probs + mask . log ( ) , dim = 1 ) # When training, sample the next step according to its probability. # During testing, we can take the greedy approach and choose highest if self . training : m = torch . distributions . Categorical ( probs ) # Sometimes an issue with Categorical & sampling on GPU; See: # https://github.com/pemami4911/neural-combinatorial-rl-pytorch/issues/5 ptr = m . sample ( ) while not torch . gather ( mask , 1 , ptr . data . unsqueeze ( 1 ) ) . byte ( ) . all ( ) : ptr = m . sample ( ) logp = m . log_prob ( ptr ) else : prob , ptr = torch . max ( probs , 1 ) # Greedy logp = prob . log ( ) # After visiting a node update the dynamic representation if self . update_fn is not None : dynamic = self . update_fn ( dynamic , ptr . data ) dynamic_hidden = self . dynamic_encoder ( dynamic ) # Since we compute the VRP in minibatches, some tours may have # number of stops. We force the vehicles to remain at the depot  # in these cases, and logp := 0 is_done = dynamic [ : , 1 ] . sum ( 1 ) . eq ( 0 ) . float ( ) logp = logp * ( 1. - is_done ) # And update the mask so we don't re-visit if we don't need to if self . mask_fn is not None : mask = self . mask_fn ( mask , dynamic , ptr . data ) . detach ( ) tour_logp . append ( logp . unsqueeze ( 1 ) ) tour_idx . append ( ptr . data . unsqueeze ( 1 ) ) decoder_input = torch . gather ( static , 2 , ptr . view ( - 1 , 1 , 1 ) . expand ( - 1 , input_size , 1 ) ) . detach ( ) tour_idx = torch . cat ( tour_idx , dim = 1 ) # (batch_size, seq_len) tour_logp = torch . cat ( tour_logp , dim = 1 ) # (batch_size, seq_len) return tour_idx , tour_logp