Does common things for validation time: computing logical form accuracy (which is expensive
        and unnecessary during training), adding visualization info to the output dictionary, etc.

        This doesn't return anything; instead it `modifies` the given ``outputs`` dictionary, and
        calls metrics on ``self``.

def _compute_validation_outputs(self,
                                    actions: List[List[ProductionRule]],
                                    best_final_states: Mapping[int, Sequence[GrammarBasedState]],
                                    world: List[WikiTablesWorld],
                                    example_lisp_string: List[str],
                                    metadata: List[Dict[str, Any]],
                                    outputs: Dict[str, Any]) -> None:
        """
        Does common things for validation time: computing logical form accuracy (which is expensive
        and unnecessary during training), adding visualization info to the output dictionary, etc.

        This doesn't return anything; instead it `modifies` the given ``outputs`` dictionary, and
        calls metrics on ``self``.
        """
        batch_size = len(actions)
        action_mapping = {}
        for batch_index, batch_actions in enumerate(actions):
            for action_index, action in enumerate(batch_actions):
                action_mapping[(batch_index, action_index)] = action[0]
        outputs['action_mapping'] = action_mapping
        outputs['best_action_sequence'] = []
        outputs['debug_info'] = []
        outputs['entities'] = []
        outputs['logical_form'] = []
        for i in range(batch_size):
            # Decoding may not have terminated with any completed logical forms, if `num_steps`
            # isn't long enough (or if the model is not trained enough and gets into an
            # infinite action loop).
            if i in best_final_states:
                best_action_indices = best_final_states[i][0].action_history[0]
                action_strings = [action_mapping[(i, action_index)] for action_index in best_action_indices]
                try:
                    logical_form = world[i].get_logical_form(action_strings, add_var_function=False)
                    self._has_logical_form(1.0)
                except ParsingError:
                    self._has_logical_form(0.0)
                    logical_form = 'Error producing logical form'
                if example_lisp_string:
                    denotation_correct = self._executor.evaluate_logical_form(logical_form,
                                                                              example_lisp_string[i])
                    self._denotation_accuracy(1.0 if denotation_correct else 0.0)
                outputs['best_action_sequence'].append(action_strings)
                outputs['logical_form'].append(logical_form)
                outputs['debug_info'].append(best_final_states[i][0].debug_info[0])  # type: ignore
                outputs['entities'].append(world[i].table_graph.entities)
            else:
                outputs['logical_form'].append('')
                self._has_logical_form(0.0)
                self._denotation_accuracy(0.0)
        if metadata is not None:
            outputs["question_tokens"] = [x["question_tokens"] for x in metadata]
            outputs["original_table"] = [x["original_table"] for x in metadata]