






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


lowercase  							= 5e-5  # larger error tolerance than in our internal tests, to avoid flaky user-facing errors





def 						__UpperCAmelCase      (					a_):
							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 			UpperCamelCase_				(      snake_case_    ):



	'''simple docstring'''




	@staticmethod
	def 							_UpperCamelCase      (	a )	->   Optional[Any]:
								snake_case_           =							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 							_UpperCamelCase      (	a	,      a )	->   str:
								snake_case_           =							set(pt_outputs.keys() )
								snake_case_           =							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(a	,      a	,      a	,      a="" ):
															# 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 ):
																						snake_case_           =							np.max(np.abs(pt_out.numpy() - tf_out.numpy() ) )
																						snake_case_           =							tensor_difference
															else:
																						snake_case_           =							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 ):
																																				snake_case_           =							root_name + pt_item
																																				snake_case_           =							tf_out[pt_item]
																																				snake_case_           =							pt_out[pt_item]
																													else:
																																				snake_case_           =							root_name + F'''[{i}]'''
																																				snake_case_           =							tf_out[i]
																													snake_case_           =							_find_pt_tf_differences(a	,      a	,      a	,      a )

															return differences

								return _find_pt_tf_differences(a	,      a	,      {} )




	def __init__(	self	,      a	,      a	,      a	,      a	,      a	,      a	,      a	,      a	,      *a	,      )	->   Optional[Any]:
								snake_case_           =							logging.get_logger('transformers-cli/pt_to_tf' )
								snake_case_           =							model_name
								snake_case_           =							local_dir if local_dir else os.path.join('/tmp'	,      a )
								snake_case_           =							max_error
								snake_case_           =							new_weights
								snake_case_           =							no_pr
								snake_case_           =							push
								snake_case_           =							extra_commit_description
								snake_case_           =							override_model_class




	def 							_UpperCamelCase      (	self	,      a	,      a	,      a )	->   Tuple:

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

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

								snake_case_           =							set(inspect.signature(pt_model.forward ).parameters.keys() )
								snake_case_           =							{}
								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:
															snake_case_           =							load_dataset('cifar10'	,      'plain_text'	,      split='test' )[:2]['img']
															processor_inputs.update({'images': sample_images} )
								if "input_features" in model_forward_signature:
															snake_case_           =							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:
																						snake_case_           =							feature_extractor_signature['padding'].default
																						if default_strategy is not False and default_strategy is not None:
																													snake_case_           =							default_strategy
																						else:
																													snake_case_           =							True
															else:
																						snake_case_           =							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} )
								snake_case_           =							processor(**a	,      return_tensors='pt' )
								snake_case_           =							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
								):
															snake_case_           =							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 							_UpperCamelCase      (	self )	->   List[Any]:
								# 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
								snake_case_           =							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
								snake_case_           =							AutoConfig.from_pretrained(self._local_dir )
								snake_case_           =							config.architectures
								if self._override_model_class is not None:
															if self._override_model_class.startswith('TF' ):
																						snake_case_           =							[self._override_model_class[2:]]
															else:
																						snake_case_           =							[self._override_model_class]
															try:
																						snake_case_           =							getattr(import_module('transformers' )	,      architectures[0] )
															except AttributeError:
																						raise ValueError(F'''Model class {self._override_model_class} not found in transformers.''' )
															try:
																						snake_case_           =							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
															snake_case_           =							getattr(import_module('transformers' )	,      'AutoModel' )
															snake_case_           =							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]}''' )
															snake_case_           =							getattr(import_module('transformers' )	,      architectures[0] )
															try:
																						snake_case_           =							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
								snake_case_           =							tf_class.from_config(a )
								snake_case_           =							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
								snake_case_           =							pt_class.from_pretrained(self._local_dir )
								pt_model.eval()

								snake_case_      ,		snake_case_           =							self.get_inputs(a	,      a	,      a )

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

								snake_case_           =							tf_class.from_pretrained(self._local_dir	,      from_pt=a )
								snake_case_           =							tf_from_pt_model(**a	,      output_hidden_states=a	,      training=a )

								# Confirms that cross loading PT weights into TF worked.
								snake_case_           =							self.find_pt_tf_differences(a	,      a )
								snake_case_           =							{k: v for k, v in crossload_differences.items() if 'hidden' not in k}
								snake_case_           =							{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\'' )
								snake_case_           =							max(output_differences.values() ) if output_differences else 0.0
								snake_case_           =							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
								snake_case_           =							os.path.join(self._local_dir	,      a )
								snake_case_           =							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

								snake_case_           =							tf_class.from_pretrained(self._local_dir )
								snake_case_           =							tf_model(**a	,      output_hidden_states=a )

								snake_case_           =							self.find_pt_tf_differences(a	,      a )
								snake_case_           =							{k: v for k, v in conversion_differences.items() if 'hidden' not in k}
								snake_case_           =							{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\'' )
								snake_case_           =							max(output_differences.values() ) if output_differences else 0.0
								snake_case_           =							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] ) )

								snake_case_           =							'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...' )
															snake_case_           =							(
															    '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 ):
																						snake_case_           =							[
																						    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:
																						snake_case_           =							[CommitOperationAdd(path_in_repo=a	,      path_or_fileobj=a )]

															snake_case_           =							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}''' )













from __future__ import annotations

from typing import Any





def 						__UpperCAmelCase      (					*a_):
							if all(_check_not_integer(a_) for m in matrix_s):
														for i in matrix_s[1:]:
																					_verify_matrix_sizes(matrix_s[0]							,					a_)
														return [[sum(a_) for t in zip(*a_)] for m in zip(*a_)]
							raise TypeError('Expected a matrix, got int/list instead')





def 						__UpperCAmelCase      (					a_							,					a_):
							if (
							    _check_not_integer(a_)
							    and _check_not_integer(a_)
							    and _verify_matrix_sizes(a_							,					a_)
							):
														return [[i - j for i, j in zip(*a_)] for m in zip(a_							,					a_)]
							raise TypeError('Expected a matrix, got int/list instead')





def 						__UpperCAmelCase      (					a_							,					a_):
							return [[x * n for x in row] for row in matrix]





def 						__UpperCAmelCase      (					a_							,					a_):
							if _check_not_integer(a_) and _check_not_integer(a_):
														snake_case_      ,		snake_case_           =							_verify_matrix_sizes(a_							,					a_)

							if cols[0] != rows[1]:
														snake_case_           =							(
														    'Cannot multiply matrix of dimensions '
														    f'''({rows[0]},{cols[0]}) and ({rows[1]},{cols[1]})'''
														)
														raise ValueError(a_)
							return [
							    [sum(m * n for m, n in zip(a_							,					a_)) for j in zip(*a_)] for i in matrix_a
							]





def 						__UpperCAmelCase      (					a_):
							snake_case_           =							int(a_)
							return [[int(row == column) for column in range(a_)] for row in range(a_)]





def 						__UpperCAmelCase      (					a_							,					a_ = True):
							if _check_not_integer(a_):
														if return_map:
																					return map(a_							,					zip(*a_))
														else:
																					return list(map(a_							,					zip(*a_)))
							raise TypeError('Expected a matrix, got int/list instead')





def 						__UpperCAmelCase      (					a_							,					a_							,					a_):
							snake_case_           =							matrix[:row] + matrix[row + 1 :]
							return [row[:column] + row[column + 1 :] for row in minor]





def 						__UpperCAmelCase      (					a_):
							if len(a_) == 1:
														return matrix[0][0]

							return sum(
							    x * determinant(minor(a_							,					0							,					a_)) * (-1) ** i
							    for i, x in enumerate(matrix[0]))





def 						__UpperCAmelCase      (					a_):
							snake_case_           =							determinant(a_)
							if det == 0:
														return None

							snake_case_           =							[
							    [determinant(minor(a_							,					a_							,					a_)) for j in range(len(a_))]
							    for i in range(len(a_))
							]

							snake_case_           =							[
							    [x * (-1) ** (row + col) for col, x in enumerate(matrix_minor[row])]
							    for row in range(len(a_))
							]
							snake_case_           =							list(transpose(a_))
							return scalar_multiply(a_							,					1 / det)





def 						__UpperCAmelCase      (					a_):
							return not isinstance(a_							,					a_) and not isinstance(matrix[0]							,					a_)





def 						__UpperCAmelCase      (					a_):
							return len(a_), len(matrix[0])





def 						__UpperCAmelCase      (					a_							,					a_):
							snake_case_           =							_shape(a_) + _shape(a_)
							if shape[0] != shape[3] or shape[1] != shape[2]:
														snake_case_           =							(
														    'operands could not be broadcast together with shape '
														    f'''({shape[0], shape[1]}), ({shape[2], shape[3]})'''
														)
														raise ValueError(a_)
							return (shape[0], shape[2]), (shape[1], shape[3])





def 						__UpperCAmelCase      (					):
							snake_case_           =							[[12, 10], [3, 9]]
							snake_case_           =							[[3, 4], [7, 4]]
							snake_case_           =							[[11, 12, 13, 14], [21, 22, 23, 24], [31, 32, 33, 34], [41, 42, 43, 44]]
							snake_case_           =							[[3, 0, 2], [2, 0, -2], [0, 1, 1]]
							print(f'''Add Operation, {add(a_							,					a_) = } \n''')
							print(
							    f'''Multiply Operation, {multiply(a_							,					a_) = } \n'''							,					)
							print(f'''Identity: {identity(5)}\n''')
							print(f'''Minor of {matrix_c} = {minor(a_							,					1							,					2)} \n''')
							print(f'''Determinant of {matrix_b} = {determinant(a_)} \n''')
							print(f'''Inverse of {matrix_d} = {inverse(a_)}\n''')


if __name__ == "__main__":
	import doctest

	doctest.testmod()
	main()





