





import inspect
import os
from argparse import ArgumentParser, Namespace
from importlib import import_module

import huggingface_hub
import numpy as np
from packaging import version

from .. import (
    FEATURE_EXTRACTOR_MAPPING,
    IMAGE_PROCESSOR_MAPPING,
    PROCESSOR_MAPPING,
    TOKENIZER_MAPPING,
    AutoConfig,
    AutoFeatureExtractor,
    AutoImageProcessor,
    AutoProcessor,
    AutoTokenizer,
    is_datasets_available,
    is_tf_available,
    is_torch_available,
)
from ..utils import TF2_WEIGHTS_INDEX_NAME, TF2_WEIGHTS_NAME, logging
from . import BaseTransformersCLICommand


if is_tf_available():
							import tensorflow as tf

							tf.config.experimental.enable_tensor_float_aa_execution(False)

if is_torch_available():
							import torch

if is_datasets_available():
							from datasets import load_dataset


UpperCAmelCase__  :	str     =5E-5  # larger error tolerance than in our internal tests, to avoid flaky user-facing errors





def    _lowercase    (  _UpperCAmelCase )  ->     Union[str, Any]:
				return PTtoTFCommand(
				    args.model_name	,					args.local_dir	,					args.max_error	,					args.new_weights	,					args.no_pr	,					args.push	,					args.extra_commit_description	,					args.override_model_class	,					)




class 					__A   (  lowerCAmelCase__			):


				@staticmethod
				def _snake_case				(						UpperCAmelCase_							):
								lowerCamelCase		       =parser.add_parser(
								    """pt-to-tf"""      ,       help=(
								        """CLI tool to run convert a transformers model from a PyTorch checkpoint to a TensorFlow checkpoint."""
								        """ Can also be used to validate existing weights without opening PRs, with --no-pr."""
								    )      ,       )
								train_parser.add_argument(
								    """--model-name"""      ,       type=_A      ,       required=_A      ,       help="""The model name, including owner/organization, as seen on the hub."""      ,       )
								train_parser.add_argument(
								    """--local-dir"""      ,       type=_A      ,       default=""""""      ,       help="""Optional local directory of the model repository. Defaults to /tmp/{model_name}"""      ,       )
								train_parser.add_argument(
								    """--max-error"""      ,       type=_A      ,       default=_A      ,       help=(
								        f"""Maximum error tolerance. Defaults to {MAX_ERROR}. This flag should be avoided, use at your own risk."""
								    )      ,       )
								train_parser.add_argument(
								    """--new-weights"""      ,       action="""store_true"""      ,       help="""Optional flag to create new TensorFlow weights, even if they already exist."""      ,       )
								train_parser.add_argument(
								    """--no-pr"""      ,       action="""store_true"""      ,       help="""Optional flag to NOT open a PR with converted weights."""							)
								train_parser.add_argument(
								    """--push"""      ,       action="""store_true"""      ,       help="""Optional flag to push the weights directly to `main` (requires permissions)"""      ,       )
								train_parser.add_argument(
								    """--extra-commit-description"""      ,       type=_A      ,       default=""""""      ,       help="""Optional additional commit description to use when opening a PR (e.g. to tag the owner)."""      ,       )
								train_parser.add_argument(
								    """--override-model-class"""      ,       type=_A      ,       default=_A      ,       help="""If you think you know better than the auto-detector, you can specify the model class here. """
								    """Can be either an AutoModel class or a specific model class like BertForSequenceClassification."""      ,       )
								train_parser.set_defaults(func=_A							)


				@staticmethod
				def _snake_case				(						UpperCAmelCase_      ,       UpperCAmelCase_							):
								lowerCamelCase		       =set(pt_outputs.keys()							)
								lowerCamelCase		       =set(tf_outputs.keys()							)
								if pt_out_attrs != tf_out_attrs:
												raise ValueError(
												    f"""The model outputs have different attributes, aborting. (Pytorch: {pt_out_attrs}, TensorFlow:"""
												    f""" {tf_out_attrs})"""							)

								# 2. For each output attribute, computes the difference
								def _find_pt_tf_differences(UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_=""							):
												# If the current attribute is a tensor, it is a leaf and we make the comparison. Otherwise, we will dig in
												# recursivelly, keeping the name of the attribute.
												if isinstance(_A      ,       torch.Tensor							):
																lowerCamelCase		       =np.max(np.abs(pt_out.numpy() - tf_out.numpy()							)							)
																lowerCamelCase		       =tensor_difference
												else:
																lowerCamelCase		       =attr_name
																for i, pt_item in enumerate(_A							):
																				# If it is a named attribute, we keep the name. Otherwise, just its index.
																				if isinstance(_A      ,       _A							):
																								lowerCamelCase		       =root_name + pt_item
																								lowerCamelCase		       =tf_out[pt_item]
																								lowerCamelCase		       =pt_out[pt_item]
																				else:
																								lowerCamelCase		       =root_name + f"""[{i}]"""
																								lowerCamelCase		       =tf_out[i]
																				lowerCamelCase		       =_find_pt_tf_differences(_A      ,       _A      ,       _A      ,       _A							)

												return differences

								return _find_pt_tf_differences(_A      ,       _A      ,       {}							)


				def __init__(						self      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       *UpperCAmelCase_      ,       ):
								lowerCamelCase		       =logging.get_logger("""transformers-cli/pt_to_tf"""							)
								lowerCamelCase		       =model_name
								lowerCamelCase		       =local_dir if local_dir else os.path.join("""/tmp"""      ,       _A							)
								lowerCamelCase		       =max_error
								lowerCamelCase		       =new_weights
								lowerCamelCase		       =no_pr
								lowerCamelCase		       =push
								lowerCamelCase		       =extra_commit_description
								lowerCamelCase		       =override_model_class


				def _snake_case				(						self      ,       UpperCAmelCase_      ,       UpperCAmelCase_      ,       UpperCAmelCase_							):

								def _get_audio_input():
												lowerCamelCase		       =load_dataset("""hf-internal-testing/librispeech_asr_dummy"""      ,       """clean"""      ,       split="""validation"""							)
												lowerCamelCase		       =ds.sort("""id"""							).select(range(2							)							)[:2]["""audio"""]
												lowerCamelCase		       =[x["""array"""] for x in speech_samples]
												return raw_samples

								lowerCamelCase		       =type(pt_model.config							)
								if model_config_class in PROCESSOR_MAPPING:
												lowerCamelCase		       =AutoProcessor.from_pretrained(self._local_dir							)
												if model_config_class in TOKENIZER_MAPPING and processor.tokenizer.pad_token is None:
																lowerCamelCase		       =processor.tokenizer.eos_token
								elif model_config_class in IMAGE_PROCESSOR_MAPPING:
												lowerCamelCase		       =AutoImageProcessor.from_pretrained(self._local_dir							)
								elif model_config_class in FEATURE_EXTRACTOR_MAPPING:
												lowerCamelCase		       =AutoFeatureExtractor.from_pretrained(self._local_dir							)
								elif model_config_class in TOKENIZER_MAPPING:
												lowerCamelCase		       =AutoTokenizer.from_pretrained(self._local_dir							)
												if processor.pad_token is None:
																lowerCamelCase		       =processor.eos_token
								else:
												raise ValueError(f"""Unknown data processing type (model config type: {model_config_class})"""							)

								lowerCamelCase		       =set(inspect.signature(pt_model.forward							).parameters.keys()							)
								lowerCamelCase		       ={}
								if "input_ids" in model_forward_signature:
												processor_inputs.update(
												    {
												        """text""": ["""Hi there!""", """I am a batch with more than one row and different input lengths."""],
												        """padding""": True,
												        """truncation""": True,
												    }							)
								if "pixel_values" in model_forward_signature:
												lowerCamelCase		       =load_dataset("""cifar10"""      ,       """plain_text"""      ,       split="""test"""							)[:2]["""img"""]
												processor_inputs.update({"""images""": sample_images}							)
								if "input_features" in model_forward_signature:
												lowerCamelCase		       =inspect.signature(processor.feature_extractor							).parameters
												# Pad to the largest input length by default but take feature extractor default
												# padding value if it exists e.g. "max_length" and is not False or None
												if "padding" in feature_extractor_signature:
																lowerCamelCase		       =feature_extractor_signature["""padding"""].default
																if default_strategy is not False and default_strategy is not None:
																				lowerCamelCase		       =default_strategy
																else:
																				lowerCamelCase		       =True
												else:
																lowerCamelCase		       =True
												processor_inputs.update({"""audio""": _get_audio_input(), """padding""": padding_strategy}							)
								if "input_values" in model_forward_signature:  # Wav2Vec2 audio input
												processor_inputs.update({"""audio""": _get_audio_input(), """padding""": True}							)
								lowerCamelCase		       =processor(**_A      ,       return_tensors="""pt"""							)
								lowerCamelCase		       =processor(**_A      ,       return_tensors="""tf"""							)

								# Extra input requirements, in addition to the input modality
								if (
								    config.is_encoder_decoder
								    or (hasattr(_A      ,       """encoder"""							) and hasattr(_A      ,       """decoder"""							))
								    or "decoder_input_ids" in tf_dummy_inputs
								):
												lowerCamelCase		       =np.asarray([[1], [1]]      ,       dtype=_A							) * (pt_model.config.decoder_start_token_id or 0)
												pt_input.update({"""decoder_input_ids""": torch.tensor(_A							)}							)
												tf_input.update({"""decoder_input_ids""": tf.convert_to_tensor(_A							)}							)

								return pt_input, tf_input


				def _snake_case				(						self							):
								# hub version 0.9.0 introduced the possibility of programmatically opening PRs with normal write tokens.
								if version.parse(huggingface_hub.__version__							) < version.parse("""0.9.0"""							):
												raise ImportError(
												    """The huggingface_hub version must be >= 0.9.0 to use this command. Please update your huggingface_hub"""
												    """ installation."""							)
								else:
												from huggingface_hub import Repository, create_commit
												from huggingface_hub._commit_api import CommitOperationAdd

								# Fetch remote data
								lowerCamelCase		       =Repository(local_dir=self._local_dir      ,       clone_from=self._model_name							)

								# Load config and get the appropriate architecture -- the latter is needed to convert the head's weights
								lowerCamelCase		       =AutoConfig.from_pretrained(self._local_dir							)
								lowerCamelCase		       =config.architectures
								if self._override_model_class is not None:
												if self._override_model_class.startswith("""TF"""							):
																lowerCamelCase		       =[self._override_model_class[2:]]
												else:
																lowerCamelCase		       =[self._override_model_class]
												try:
																lowerCamelCase		       =getattr(import_module("""transformers"""							)      ,       architectures[0]							)
												except AttributeError:
																raise ValueError(f"""Model class {self._override_model_class} not found in transformers."""							)
												try:
																lowerCamelCase		       =getattr(import_module("""transformers"""							)      ,       """TF""" + architectures[0]							)
												except AttributeError:
																raise ValueError(f"""TF model class TF{self._override_model_class} not found in transformers."""							)
								elif architectures is None:  # No architecture defined -- use auto classes
												lowerCamelCase		       =getattr(import_module("""transformers"""							)      ,       """AutoModel"""							)
												lowerCamelCase		       =getattr(import_module("""transformers"""							)      ,       """TFAutoModel"""							)
												self._logger.warning("""No detected architecture, using AutoModel/TFAutoModel"""							)
								else:  # Architecture defined -- use it
												if len(_A							) > 1:
																raise ValueError(f"""More than one architecture was found, aborting. (architectures = {architectures})"""							)
												self._logger.warning(f"""Detected architecture: {architectures[0]}"""							)
												lowerCamelCase		       =getattr(import_module("""transformers"""							)      ,       architectures[0]							)
												try:
																lowerCamelCase		       =getattr(import_module("""transformers"""							)      ,       """TF""" + architectures[0]							)
												except AttributeError:
																raise AttributeError(f"""The TensorFlow equivalent of {architectures[0]} doesn't exist in transformers."""							)

        # Check the TF dummy inputs to see what keys we need in the forward pass
								lowerCamelCase		       =tf_class.from_config(_A							)
								lowerCamelCase		       =tf_from_pt_model.dummy_inputs

								del tf_from_pt_model  # Try to keep only one model in memory at a time

								# Load the model and get some basic inputs
								lowerCamelCase		       =pt_class.from_pretrained(self._local_dir							)
								pt_model.eval()

								lowerCamelCase       ,     lowerCamelCase		       =self.get_inputs(_A      ,       _A      ,       _A							)

								with torch.no_grad():
												lowerCamelCase		       =pt_model(**_A      ,       output_hidden_states=_A							)
								del pt_model  # will no longer be used, and may have a large memory footprint

								lowerCamelCase		       =tf_class.from_pretrained(self._local_dir      ,       from_pt=_A							)
								lowerCamelCase		       =tf_from_pt_model(**_A      ,       output_hidden_states=_A      ,       training=_A							)

								# Confirms that cross loading PT weights into TF worked.
								lowerCamelCase		       =self.find_pt_tf_differences(_A      ,       _A							)
								lowerCamelCase		       ={k: v for k, v in crossload_differences.items() if """hidden""" not in k}
								lowerCamelCase		       ={k: v for k, v in crossload_differences.items() if """hidden""" in k}
								if len(_A							) == 0 and architectures is not None:
												raise ValueError(
												    f"""Something went wrong -- the config file has architectures ({architectures}), but no model head"""
												    """ output was found. All outputs start with \'hidden\'"""							)
								lowerCamelCase		       =max(output_differences.values()							) if output_differences else 0.0
								lowerCamelCase		       =max(hidden_differences.values()							)
								if max_crossload_output_diff > self._max_error or max_crossload_hidden_diff > self._max_error:
												raise ValueError(
												    """The cross-loaded TensorFlow model has different outputs, something went wrong!\n"""
												    + f"""\nList of maximum output differences above the threshold ({self._max_error}):\n"""
												    + """\n""".join([f"""{k}: {v:.3e}""" for k, v in output_differences.items() if v > self._max_error]							)
												    + f"""\n\nList of maximum hidden layer differences above the threshold ({self._max_error}):\n"""
												    + """\n""".join([f"""{k}: {v:.3e}""" for k, v in hidden_differences.items() if v > self._max_error]							)							)

								# Save the weights in a TF format (if needed) and confirms that the results are still good
								lowerCamelCase		       =os.path.join(self._local_dir      ,       _A							)
								lowerCamelCase		       =os.path.join(self._local_dir      ,       _A							)
								if (not os.path.exists(_A							) and not os.path.exists(_A							)) or self._new_weights:
												tf_from_pt_model.save_pretrained(self._local_dir							)
								del tf_from_pt_model  # will no longer be used, and may have a large memory footprint

								lowerCamelCase		       =tf_class.from_pretrained(self._local_dir							)
								lowerCamelCase		       =tf_model(**_A      ,       output_hidden_states=_A							)

								lowerCamelCase		       =self.find_pt_tf_differences(_A      ,       _A							)
								lowerCamelCase		       ={k: v for k, v in conversion_differences.items() if """hidden""" not in k}
								lowerCamelCase		       ={k: v for k, v in conversion_differences.items() if """hidden""" in k}
								if len(_A							) == 0 and architectures is not None:
												raise ValueError(
												    f"""Something went wrong -- the config file has architectures ({architectures}), but no model head"""
												    """ output was found. All outputs start with \'hidden\'"""							)
								lowerCamelCase		       =max(output_differences.values()							) if output_differences else 0.0
								lowerCamelCase		       =max(hidden_differences.values()							)
								if max_conversion_output_diff > self._max_error or max_conversion_hidden_diff > self._max_error:
												raise ValueError(
												    """The converted TensorFlow model has different outputs, something went wrong!\n"""
												    + f"""\nList of maximum output differences above the threshold ({self._max_error}):\n"""
												    + """\n""".join([f"""{k}: {v:.3e}""" for k, v in output_differences.items() if v > self._max_error]							)
												    + f"""\n\nList of maximum hidden layer differences above the threshold ({self._max_error}):\n"""
												    + """\n""".join([f"""{k}: {v:.3e}""" for k, v in hidden_differences.items() if v > self._max_error]							)							)

								lowerCamelCase		       ="""Update TF weights""" if self._new_weights else """Add TF weights"""
								if self._push:
												repo.git_add(auto_lfs_track=_A							)
												repo.git_commit(_A							)
												repo.git_push(blocking=_A							)  # this prints a progress bar with the upload
												self._logger.warning(f"""TF weights pushed into {self._model_name}"""							)
								elif not self._no_pr:
												self._logger.warning("""Uploading the weights into a new PR..."""							)
												lowerCamelCase		       =(
												    """Model converted by the [`transformers`\' `pt_to_tf`"""
												    """ CLI](https://github.com/huggingface/transformers/blob/main/src/transformers/commands/pt_to_tf.py). """
												    """All converted model outputs and hidden layers were validated against its PyTorch counterpart.\n\n"""
												    f"""Maximum crossload output difference={max_crossload_output_diff:.3e}; """
												    f"""Maximum crossload hidden layer difference={max_crossload_hidden_diff:.3e};\n"""
												    f"""Maximum conversion output difference={max_conversion_output_diff:.3e}; """
												    f"""Maximum conversion hidden layer difference={max_conversion_hidden_diff:.3e};\n"""
												)
												if self._max_error > MAX_ERROR:
																commit_descrition += (
																    f"""\n\nCAUTION: The maximum admissible error was manually increased to {self._max_error}!"""
																)
												if self._extra_commit_description:
																commit_descrition += "\n\n" + self._extra_commit_description

												# sharded model -> adds all related files (index and .h5 shards)
												if os.path.exists(_A							):
																lowerCamelCase		       =[
																    CommitOperationAdd(path_in_repo=_A      ,       path_or_fileobj=_A							)
																]
																for shard_path in tf.io.gfile.glob(self._local_dir + """/tf_model-*.h5"""							):
																				operations += [
																				    CommitOperationAdd(path_in_repo=os.path.basename(_A							)      ,       path_or_fileobj=_A							)
																				]
												else:
																lowerCamelCase		       =[CommitOperationAdd(path_in_repo=_A      ,       path_or_fileobj=_A							)]

												lowerCamelCase		       =create_commit(
												    repo_id=self._model_name      ,       operations=_A      ,       commit_message=_A      ,       commit_description=_A      ,       repo_type="""model"""      ,       create_pr=_A      ,       ).pr_url
												self._logger.warning(f"""PR open in {hub_pr_url}"""							)








def     _UpperCAmelCase				(     _SCREAMING_SNAKE_CASE    :						str				):





    """simple docstring"""


    return "".join(chr(ord(_SCREAMING_SNAKE_CASE				) + 32				) if 'A' <= char <= 'Z' else char for char in word				)


if __name__ == "__main__":
       from doctest import testmod

       testmod()


