
import logging
import math
from functools import partial
from typing import Any, Callable, Dict, Iterable, List, Optional, Sequence, Tuple, Union

import torch

from .tensor_utils import tensor_tree_map, tree_map



def        UpperCamelCase    (							lowerCAmelCase__		):
					'''simple docstring'''
					lowercase							=						[]
					if isinstance(__UpperCamelCase ,			__UpperCamelCase		):
										for v in tree.values():
															shapes.extend(_fetch_dims(__UpperCamelCase		)		)
					elif isinstance(__UpperCamelCase ,			(list, tuple)		):
										for t in tree:
															shapes.extend(_fetch_dims(__UpperCamelCase		)		)
					elif isinstance(__UpperCamelCase ,			torch.Tensor		):
										shapes.append(tree.shape		)
					else:
										raise ValueError('''Not supported'''		)

					return shapes



@torch.jit.ignore
def        UpperCamelCase    (							lowerCAmelCase__ ,			lowerCAmelCase__		):
					'''simple docstring'''
					lowercase							=						[]
					for d in reversed(__UpperCamelCase		):
										idx.append(flat_idx % d		)
										lowercase							=						flat_idx // d

					return tuple(reversed(__UpperCamelCase		)		)



@torch.jit.ignore
def        UpperCamelCase    (							lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__ = None ,			lowerCAmelCase__ = None ,			):
					'''simple docstring'''







					def reduce_edge_list(lowerCAmelCase__		) -> None:
										lowercase							=						True
										for i in range(len(__UpperCamelCase		)		):
															lowercase							=						-1 * (i + 1)
															l[reversed_idx] &= tally
															lowercase							=						l[reversed_idx]

					if start_edges is None:
										lowercase							=						[s == 0 for s in start]
										reduce_edge_list(__UpperCamelCase		)
					if end_edges is None:
										lowercase							=						[e == (d - 1) for e, d in zip(__UpperCamelCase ,			__UpperCamelCase		)]
										reduce_edge_list(__UpperCamelCase		)

					# Base cases. Either start/end are empty and we're done, or the final,
					# one-dimensional tensor can be simply sliced
					if len(__UpperCamelCase		) == 0:
										return [()]
					elif len(__UpperCamelCase		) == 1:
										return [(slice(start[0] ,			end[0] + 1		),)]

					lowercase							=						[]
					lowercase							=						[]

					# Dimensions common to start and end can be selected directly
					for s, e in zip(__UpperCamelCase ,			__UpperCamelCase		):
										if s == e:
															path_list.append(slice(__UpperCamelCase ,			s + 1		)		)
										else:
															break

					lowercase							=						tuple(__UpperCamelCase		)
					lowercase							=						len(__UpperCamelCase		)

					# start == end, and we're done
					if divergence_idx == len(__UpperCamelCase		):
										return [path]

					def upper() -> Tuple[Tuple[slice, ...], ...]:
										assert start_edges is not None
										assert end_edges is not None

										lowercase							=						start[divergence_idx]
										return tuple(
										    path + (slice(__UpperCamelCase ,			sdi + 1		),) + s
										    for s in _get_minimal_slice_set(
										        start[divergence_idx + 1 :] ,			[d - 1 for d in dims[divergence_idx + 1 :]] ,			dims[divergence_idx + 1 :] ,			start_edges=start_edges[divergence_idx + 1 :] ,			end_edges=[True for _ in end_edges[divergence_idx + 1 :]] ,			)		)

					def lower() -> Tuple[Tuple[slice, ...], ...]:
										assert start_edges is not None
										assert end_edges is not None

										lowercase							=						end[divergence_idx]
										return tuple(
										    path + (slice(__UpperCamelCase ,			edi + 1		),) + s
										    for s in _get_minimal_slice_set(
										        [0 for _ in start[divergence_idx + 1 :]] ,			end[divergence_idx + 1 :] ,			dims[divergence_idx + 1 :] ,			start_edges=[True for _ in start_edges[divergence_idx + 1 :]] ,			end_edges=end_edges[divergence_idx + 1 :] ,			)		)

					# If both start and end are at the edges of the subtree rooted at
					# divergence_idx, we can just select the whole subtree at once
					if start_edges[divergence_idx] and end_edges[divergence_idx]:
										slices.append(path + (slice(start[divergence_idx] ,			end[divergence_idx] + 1		),)		)
					# If just start is at the edge, we can grab almost all of the subtree,
					# treating only the ragged bottom edge as an edge case
					elif start_edges[divergence_idx]:
										slices.append(path + (slice(start[divergence_idx] ,			end[divergence_idx]		),)		)
										slices.extend(lower()		)
					# Analogous to the previous case, but the top is ragged this time
					elif end_edges[divergence_idx]:
										slices.extend(upper()		)
										slices.append(path + (slice(start[divergence_idx] + 1 ,			end[divergence_idx] + 1		),)		)
					# If both sides of the range are ragged, we need to handle both sides
					# separately. If there's contiguous meat in between them, we can index it
					# in one big chunk
					else:
										slices.extend(upper()		)
										lowercase							=						end[divergence_idx] - start[divergence_idx]
										if middle_ground > 1:
															slices.append(path + (slice(start[divergence_idx] + 1 ,			end[divergence_idx]		),)		)
										slices.extend(lower()		)

					return slices



@torch.jit.ignore
def        UpperCamelCase    (							lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__		):
					'''simple docstring'''
					lowercase							=						t.shape[:no_batch_dims]
					lowercase							=						list(_flat_idx_to_idx(__UpperCamelCase ,			__UpperCamelCase		)		)
					# _get_minimal_slice_set is inclusive
					lowercase							=						list(_flat_idx_to_idx(flat_end - 1 ,			__UpperCamelCase		)		)

					# Get an ordered list of slices to perform
					lowercase							=						_get_minimal_slice_set(
					    __UpperCamelCase ,			__UpperCamelCase ,			__UpperCamelCase ,			)

					lowercase							=						[t[s] for s in slices]

					return torch.cat([s.view((-1,) + t.shape[no_batch_dims:]		) for s in sliced_tensors]		)



def        UpperCamelCase    (							lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__ ,			lowerCAmelCase__ = False ,			lowerCAmelCase__ = None ,			lowerCAmelCase__ = False ,			):
					'''simple docstring'''







					if not (len(__UpperCamelCase		) > 0):
										raise ValueError('''Must provide at least one input'''		)

					lowercase							=						[shape[:no_batch_dims] for shape in _fetch_dims(__UpperCamelCase		)]
					lowercase							=						tuple([max(__UpperCamelCase		) for s in zip(*__UpperCamelCase		)]		)

					def _prep_inputs(lowerCAmelCase__		) -> torch.Tensor:
										if not low_mem:
															if not sum(t.shape[:no_batch_dims]		) == no_batch_dims:
																				lowercase							=						t.expand(orig_batch_dims + t.shape[no_batch_dims:]		)
															lowercase							=						t.reshape(-1 ,			*t.shape[no_batch_dims:]		)
										else:
															lowercase							=						t.expand(orig_batch_dims + t.shape[no_batch_dims:]		)
										return t

					lowercase							=						tensor_tree_map(_prep_inputs ,			__UpperCamelCase		)
					lowercase							=						None
					if _out is not None:
										lowercase							=						tensor_tree_map(lambda lowerCAmelCase__		: t.view([-1] + list(t.shape[no_batch_dims:]		)		) ,			_out		)

					lowercase							=						1
					for d in orig_batch_dims:
										flat_batch_dim *= d

					lowercase							=						flat_batch_dim // chunk_size + (flat_batch_dim % chunk_size != 0)

					def _select_chunk(lowerCAmelCase__		) -> torch.Tensor:
										return t[i : i + chunk_size] if t.shape[0] != 1 else t

					lowercase							=						0
					lowercase							=						prepped_outputs
					for _ in range(__UpperCamelCase		):
										# Chunk the input
										if not low_mem:
															lowercase							=						_select_chunk
										else:
															lowercase							=						partial(
															    _chunk_slice ,			flat_start=__UpperCamelCase ,			flat_end=min(__UpperCamelCase ,			i + chunk_size		) ,			no_batch_dims=len(__UpperCamelCase		) ,			)

										lowercase							=						tensor_tree_map(__UpperCamelCase ,			__UpperCamelCase		)

										# Run the layer on the chunk
										lowercase							=						layer(**__UpperCamelCase		)

										# Allocate space for the output
										if out is None:
															lowercase							=						tensor_tree_map(lambda lowerCAmelCase__		: t.new_zeros((flat_batch_dim,) + t.shape[1:]		) ,			__UpperCamelCase		)

										# Put the chunk in its pre-allocated space
										if isinstance(__UpperCamelCase ,			__UpperCamelCase		):

															def assign(lowerCAmelCase__ ,			lowerCAmelCase__		) -> None:
																				for k, v in da.items():
																									if isinstance(__UpperCamelCase ,			__UpperCamelCase		):
																														assign(__UpperCamelCase ,			da[k]		)
																									else:
																														if _add_into_out:
																																			v[i : i + chunk_size] += da[k]
																														else:
																																			lowercase							=						da[k]

															assign(__UpperCamelCase ,			__UpperCamelCase		)
										elif isinstance(__UpperCamelCase ,			__UpperCamelCase		):
															for xa, xa in zip(__UpperCamelCase ,			__UpperCamelCase		):
																				if _add_into_out:
																									xa[i : i + chunk_size] += xa
																				else:
																									lowercase							=						xa
										elif isinstance(__UpperCamelCase ,			torch.Tensor		):
															if _add_into_out:
																				out[i : i + chunk_size] += output_chunk
															else:
																				lowercase							=						output_chunk
										else:
															raise ValueError('''Not supported'''		)

										i += chunk_size

					lowercase							=						tensor_tree_map(lambda lowerCAmelCase__		: t.view(orig_batch_dims + t.shape[1:]		) ,			__UpperCamelCase		)

					return out







class 				lowercase		:




							def __init__(				self   ,A__ = 5_1_2   ,):
												lowercase							=						max_chunk_size
												lowercase							=						None
												lowercase							=						None




							def  A__      (				self   ,A__   ,A__   ,A__):
												logging.info('''Tuning chunk size...''')

												if min_chunk_size >= self.max_chunk_size:
																	return min_chunk_size

												lowercase							=						[2**l for l in range(int(math.log(self.max_chunk_size   ,2)) + 1)]
												lowercase							=						[c for c in candidates if c > min_chunk_size]
												lowercase							=						[min_chunk_size] + candidates
												candidates[-1] += 4

												def test_chunk_size(A__) -> bool:
																	try:
																						with torch.no_grad():
																											fn(*_lowerCamelCase   ,chunk_size=_lowerCamelCase)
																						return True
																	except RuntimeError:
																						return False

												lowercase							=						0
												lowercase							=						len(_lowerCamelCase) - 1
												while i > min_viable_chunk_size_index:
																	lowercase							=						test_chunk_size(candidates[i])
																	if not viable:
																						lowercase							=						(min_viable_chunk_size_index + i) // 2
																	else:
																						lowercase							=						i
																						lowercase							=						(i + len(_lowerCamelCase) - 1) // 2

												return candidates[min_viable_chunk_size_index]




							def  A__      (				self   ,A__   ,A__):
												lowercase							=						True
												for aa, aa in zip(_lowerCamelCase   ,_lowerCamelCase):
																	assert type(_lowerCamelCase) == type(_lowerCamelCase)
																	if isinstance(_lowerCamelCase   ,(list, tuple)):
																						consistent &= self._compare_arg_caches(_lowerCamelCase   ,_lowerCamelCase)
																	elif isinstance(_lowerCamelCase   ,_lowerCamelCase):
																						lowercase							=						[v for _, v in sorted(aa.items()   ,key=lambda A__: x[0])]
																						lowercase							=						[v for _, v in sorted(aa.items()   ,key=lambda A__: x[0])]
																						consistent &= self._compare_arg_caches(_lowerCamelCase   ,_lowerCamelCase)
																	else:
																						consistent &= aa == aa

												return consistent





							def  A__      (				self   ,A__   ,A__   ,A__   ,):
												lowercase							=						True
												lowercase							=						tree_map(lambda A__: a.shape if isinstance(_lowerCamelCase   ,torch.Tensor) else a   ,_lowerCamelCase   ,_lowerCamelCase)
												if self.cached_arg_data is not None:
																	# If args have changed shape/value, we need to re-tune
																	assert len(self.cached_arg_data) == len(_lowerCamelCase)
																	lowercase							=						self._compare_arg_caches(self.cached_arg_data   ,_lowerCamelCase)
												else:
																	# Otherwise, we can reuse the precomputed value
																	lowercase							=						False

												if not consistent:
																	lowercase							=						self._determine_favorable_chunk_size(
																	    _lowerCamelCase   ,_lowerCamelCase   ,_lowerCamelCase   ,)
																	lowercase							=						arg_data

												assert self.cached_chunk_size is not None

												return self.cached_chunk_size












"""simple docstring"""


import warnings

from ...utils import logging
from .image_processing_segformer import SegformerImageProcessor


lowercase_				     =     logging.get_logger(__name__)


class 					snake_case		(							_lowerCAmelCase						):


   '''simple docstring'''

   def __init__(	self :     Optional[int],       *_lowerCamelCase :     Union[str, Any],       **_lowerCamelCase :     Dict  ):

     '''simple docstring'''
     warnings.warn(
         '''The class SegformerFeatureExtractor is deprecated and will be removed in version 5 of Transformers.'''
         ''' Please use SegformerImageProcessor instead.''',       _lowerCamelCase,       )
     super().__init__(*_lowerCamelCase,       **_lowerCamelCase  )




