Base implementation of a pattern over a sequence with multiple codebooks. The codebook pattern consists in a layout, defining for each sequence step the list of coordinates of each codebook timestep in the resulting interleaved sequence. The first item of the pattern is always an empty
| 17 | |
| 18 | @dataclass |
| 19 | class Pattern: |
| 20 | """Base implementation of a pattern over a sequence with multiple codebooks. |
| 21 | |
| 22 | The codebook pattern consists in a layout, defining for each sequence step |
| 23 | the list of coordinates of each codebook timestep in the resulting interleaved sequence. |
| 24 | The first item of the pattern is always an empty list in order to properly insert a special token |
| 25 | to start with. For convenience, we also keep track of ``n_q`` the number of codebooks used for the pattern |
| 26 | and ``timesteps`` the number of timesteps corresponding to the original sequence. |
| 27 | |
| 28 | The pattern provides convenient methods to build and revert interleaved sequences from it: |
| 29 | ``build_pattern_sequence`` maps a given a dense input tensor of multi-codebook sequence from [B, K, T] |
| 30 | to the interleaved sequence of shape [B, K, S] applying the pattern, with B being the batch size, |
| 31 | K being the number of codebooks, T the number of original timesteps and S the number of sequence steps |
| 32 | for the output sequence. The unfilled positions are replaced with a special token and the built sequence |
| 33 | is returned along with a mask indicating valid tokens. |
| 34 | ``revert_pattern_sequence`` maps back an interleaved sequence of shape [B, K, S] to the original alignment |
| 35 | of codebooks across timesteps to an output tensor of shape [B, K, T], using again a special token and a mask |
| 36 | to fill and specify invalid positions if needed. |
| 37 | See the dedicated methods for more details. |
| 38 | """ |
| 39 | # Pattern layout, for each sequence step, we have a list of coordinates |
| 40 | # corresponding to the original codebook timestep and position. |
| 41 | # The first list is always an empty list in order to properly insert |
| 42 | # a special token to start with. |
| 43 | layout: PatternLayout |
| 44 | timesteps: int |
| 45 | n_q: int |
| 46 | |
| 47 | def __post_init__(self): |
| 48 | assert len(self.layout) > 0 |
| 49 | self._validate_layout() |
| 50 | self._build_reverted_sequence_scatter_indexes = lru_cache(100)(self._build_reverted_sequence_scatter_indexes) |
| 51 | self._build_pattern_sequence_scatter_indexes = lru_cache(100)(self._build_pattern_sequence_scatter_indexes) |
| 52 | logger.info("New pattern, time steps: %d, sequence steps: %d", self.timesteps, len(self.layout)) |
| 53 | |
| 54 | def _validate_layout(self): |
| 55 | """Runs checks on the layout to ensure a valid pattern is defined. |
| 56 | A pattern is considered invalid if: |
| 57 | - Multiple timesteps for a same codebook are defined in the same sequence step |
| 58 | - The timesteps for a given codebook are not in ascending order as we advance in the sequence |
| 59 | (this would mean that we have future timesteps before past timesteps). |
| 60 | """ |
| 61 | q_timesteps = {q: 0 for q in range(self.n_q)} |
| 62 | for s, seq_coords in enumerate(self.layout): |
| 63 | if len(seq_coords) > 0: |
| 64 | qs = set() |
| 65 | for coord in seq_coords: |
| 66 | qs.add(coord.q) |
| 67 | last_q_timestep = q_timesteps[coord.q] |
| 68 | assert coord.t >= last_q_timestep, \ |
| 69 | f"Past timesteps are found in the sequence for codebook = {coord.q} at step {s}" |
| 70 | q_timesteps[coord.q] = coord.t |
| 71 | # each sequence step contains at max 1 coordinate per codebook |
| 72 | assert len(qs) == len(seq_coords), \ |
| 73 | f"Multiple entries for a same codebook are found at step {s}" |
| 74 | |
| 75 | @property |
| 76 | def num_sequence_steps(self): |
no outgoing calls
no test coverage detected