


import os
import shutil
from pathlib import Path
from typing import Optional, Union

import numpy as np
from huggingface_hub import hf_hub_download

from ..utils import ONNX_EXTERNAL_WEIGHTS_NAME, ONNX_WEIGHTS_NAME, is_onnx_available, logging


if is_onnx_available():
	import onnxruntime as ort


A	:		List[Any]    =       logging.get_logger(__name__)

A	:		str    =       {
    'tensor(bool)': np.bool_,
    'tensor(int8)': np.inta,
    'tensor(uint8)': np.uinta,
    'tensor(int16)': np.intaa,
    'tensor(uint16)': np.uintaa,
    'tensor(int32)': np.intaa,
    'tensor(uint32)': np.uintaa,
    'tensor(int64)': np.intaa,
    'tensor(uint64)': np.uintaa,
    'tensor(float16)': np.floataa,
    'tensor(float)': np.floataa,
    'tensor(double)': np.floataa,
}



class 							A   :
					'''simple docstring'''






					def __init__(self   :						Optional[int]				, _UpperCAmelCase   :						Any=None				, **_UpperCAmelCase   :						Optional[int]		)						->       Union[str, Any]:




							"""simple docstring"""


							logger.info("""`diffusers.OnnxRuntimeModel` is experimental and might change in the future."""		)
							lowercase__					      =		model
							lowercase__					      =		kwargs.get("""model_save_dir"""				, _UpperCAmelCase		)
							lowercase__					      =		kwargs.get("""latest_model_name"""				, _UpperCAmelCase		)

					def __call__(self   :						int				, **_UpperCAmelCase   :						Optional[int]		)						->       str:




							"""simple docstring"""


							lowercase__					      =		{k: np.array(_UpperCAmelCase		) for k, v in kwargs.items()}
							return self.model.run(_UpperCAmelCase				, _UpperCAmelCase		)

					@staticmethod
					def   lowerCamelCase__					(_UpperCAmelCase   :						Union[str, Path]				, _UpperCAmelCase   :						Union[str, Any]=None				, _UpperCAmelCase   :						List[Any]=None		)						->       Optional[int]:




							"""simple docstring"""


							if provider is None:
									logger.info("""No onnxruntime provider specified, using CPUExecutionProvider"""		)
									lowercase__					      =		"""CPUExecutionProvider"""

							return ort.InferenceSession(_UpperCAmelCase				, providers=[provider]				, sess_options=_UpperCAmelCase		)

					def   lowerCamelCase__					(self   :						str				, _UpperCAmelCase   :						Union[str, Path]				, _UpperCAmelCase   :						Optional[str] = None				, **_UpperCAmelCase   :						Any		)						->       List[str]:




							"""simple docstring"""


							lowercase__					      =		file_name if file_name is not None else ONNX_WEIGHTS_NAME

							lowercase__					      =		self.model_save_dir.joinpath(self.latest_model_name		)
							lowercase__					      =		Path(_UpperCAmelCase		).joinpath(_UpperCAmelCase		)
							try:
									shutil.copyfile(_UpperCAmelCase				, _UpperCAmelCase		)
							except shutil.SameFileError:
									pass

							# copy external weights (for models >2GB)
							lowercase__					      =		self.model_save_dir.joinpath(_UpperCAmelCase		)
							if src_path.exists():
									lowercase__					      =		Path(_UpperCAmelCase		).joinpath(_UpperCAmelCase		)
									try:
											shutil.copyfile(_UpperCAmelCase				, _UpperCAmelCase		)
									except shutil.SameFileError:
											pass

					def   lowerCamelCase__					(self   :						Union[str, Any]				, _UpperCAmelCase   :						Union[str, os.PathLike]				, **_UpperCAmelCase   :						int				, )						->       Optional[int]:




							"""simple docstring"""


							if os.path.isfile(_UpperCAmelCase		):
									logger.error(f'''Provided path ({save_directory}) should be a directory, not a file'''		)
									return

							os.makedirs(_UpperCAmelCase				, exist_ok=_UpperCAmelCase		)

							# saving model weights/files
							self._save_pretrained(_UpperCAmelCase				, **_UpperCAmelCase		)

					@classmethod
					def   lowerCamelCase__					(cls   :						List[Any]				, _UpperCAmelCase   :						Union[str, Path]				, _UpperCAmelCase   :						Optional[Union[bool, str, None]] = None				, _UpperCAmelCase   :						Optional[Union[str, None]] = None				, _UpperCAmelCase   :						bool = False				, _UpperCAmelCase   :						Optional[str] = None				, _UpperCAmelCase   :						Optional[str] = None				, _UpperCAmelCase   :						Optional[str] = None				, _UpperCAmelCase   :						Optional["ort.SessionOptions"] = None				, **_UpperCAmelCase   :						Union[str, Any]				, )						->       int:




							"""simple docstring"""


							lowercase__					      =		file_name if file_name is not None else ONNX_WEIGHTS_NAME
							# load model from local directory
							if os.path.isdir(_UpperCAmelCase		):
									lowercase__					      =		OnnxRuntimeModel.load_model(
									    os.path.join(_UpperCAmelCase				, _UpperCAmelCase		)				, provider=_UpperCAmelCase				, sess_options=_UpperCAmelCase		)
									lowercase__					      =		Path(_UpperCAmelCase		)
							# load model from hub
							else:
									# download model
									lowercase__					      =		hf_hub_download(
									    repo_id=_UpperCAmelCase				, filename=_UpperCAmelCase				, use_auth_token=_UpperCAmelCase				, revision=_UpperCAmelCase				, cache_dir=_UpperCAmelCase				, force_download=_UpperCAmelCase				, )
									lowercase__					      =		Path(_UpperCAmelCase		).parent
									lowercase__					      =		Path(_UpperCAmelCase		).name
									lowercase__					      =		OnnxRuntimeModel.load_model(_UpperCAmelCase				, provider=_UpperCAmelCase				, sess_options=_UpperCAmelCase		)
							return cls(model=_UpperCAmelCase				, **_UpperCAmelCase		)


					@classmethod
					def   lowerCamelCase__					(cls   :						int				, _UpperCAmelCase   :						Union[str, Path]				, _UpperCAmelCase   :						bool = True				, _UpperCAmelCase   :						Optional[str] = None				, _UpperCAmelCase   :						Optional[str] = None				, **_UpperCAmelCase   :						Optional[int]				, )						->       str:




							"""simple docstring"""


							lowercase__					      =		None
							if len(str(_UpperCAmelCase		).split("""@"""		)		) == 2:
									lowercase__      ,     lowercase__					      =		model_id.split("""@"""		)

							return cls._from_pretrained(
							    model_id=_UpperCAmelCase				, revision=_UpperCAmelCase				, cache_dir=_UpperCAmelCase				, force_download=_UpperCAmelCase				, use_auth_token=_UpperCAmelCase				, **_UpperCAmelCase				, )





import inspect
from typing import Callable, List, Optional, Union

import torch
from transformers import CLIPImageProcessor, CLIPTextModel, CLIPTokenizer

from diffusers import DiffusionPipeline
from diffusers.models import AutoencoderKL, UNetaDConditionModel
from diffusers.pipelines.stable_diffusion import StableDiffusionPipelineOutput
from diffusers.pipelines.stable_diffusion.safety_checker import StableDiffusionSafetyChecker
from diffusers.schedulers import DDIMScheduler, LMSDiscreteScheduler, PNDMScheduler
from diffusers.utils import logging


A	:		str    =       logging.get_logger(__name__)  # pylint: disable=invalid-name



class 							A   (	UpperCAmelCase__							):
					'''simple docstring'''






					def __init__(self   :						List[str]				, _UpperCAmelCase   :						AutoencoderKL				, _UpperCAmelCase   :						CLIPTextModel				, _UpperCAmelCase   :						CLIPTokenizer				, _UpperCAmelCase   :						UNetaDConditionModel				, _UpperCAmelCase   :						Union[DDIMScheduler, PNDMScheduler, LMSDiscreteScheduler]				, _UpperCAmelCase   :						StableDiffusionSafetyChecker				, _UpperCAmelCase   :						CLIPImageProcessor				, )						->       Dict:




							"""simple docstring"""


							super().__init__()
							self.register_modules(
							    vae=_UpperCAmelCase				, text_encoder=_UpperCAmelCase				, tokenizer=_UpperCAmelCase				, unet=_UpperCAmelCase				, scheduler=_UpperCAmelCase				, safety_checker=_UpperCAmelCase				, feature_extractor=_UpperCAmelCase				, )

					def   lowerCamelCase__					(self   :						Optional[Any]				, _UpperCAmelCase   :						Optional[Union[str, int]] = "auto"		)						->       Tuple:




							"""simple docstring"""


							if slice_size == "auto":
									# half the attention head size is usually a good trade-off between
									# speed and memory
									lowercase__					      =		self.unet.config.attention_head_dim // 2
							self.unet.set_attention_slice(_UpperCAmelCase		)

					def   lowerCamelCase__					(self   :						Optional[Any]		)						->       Optional[Any]:




							"""simple docstring"""


							self.enable_attention_slicing(_UpperCAmelCase		)


					@torch.no_grad()
					def __call__(self   :						Any				, _UpperCAmelCase   :						Union[str, List[str]]				, _UpperCAmelCase   :						int = 512				, _UpperCAmelCase   :						int = 512				, _UpperCAmelCase   :						int = 50				, _UpperCAmelCase   :						float = 7.5				, _UpperCAmelCase   :						Optional[Union[str, List[str]]] = None				, _UpperCAmelCase   :						Optional[int] = 1				, _UpperCAmelCase   :						float = 0.0				, _UpperCAmelCase   :						Optional[torch.Generator] = None				, _UpperCAmelCase   :						Optional[torch.FloatTensor] = None				, _UpperCAmelCase   :						Optional[str] = "pil"				, _UpperCAmelCase   :						bool = True				, _UpperCAmelCase   :						Optional[Callable[[int, int, torch.FloatTensor], None]] = None				, _UpperCAmelCase   :						int = 1				, _UpperCAmelCase   :						Optional[torch.FloatTensor] = None				, **_UpperCAmelCase   :						Any				, )						->       Tuple:




							"""simple docstring"""


							if isinstance(_UpperCAmelCase				, _UpperCAmelCase		):
									lowercase__					      =		1
							elif isinstance(_UpperCAmelCase				, _UpperCAmelCase		):
									lowercase__					      =		len(_UpperCAmelCase		)
							else:
									raise ValueError(f'''`prompt` has to be of type `str` or `list` but is {type(_UpperCAmelCase		)}'''		)

							if height % 8 != 0 or width % 8 != 0:
									raise ValueError(f'''`height` and `width` have to be divisible by 8 but are {height} and {width}.'''		)

							if (callback_steps is None) or (
							    callback_steps is not None and (not isinstance(_UpperCAmelCase				, _UpperCAmelCase		) or callback_steps <= 0)
							):
									raise ValueError(
									    f'''`callback_steps` has to be a positive integer but is {callback_steps} of type'''
									    f''' {type(_UpperCAmelCase		)}.'''		)

							# get prompt text embeddings
							lowercase__					      =		self.tokenizer(
							    _UpperCAmelCase				, padding="""max_length"""				, max_length=self.tokenizer.model_max_length				, return_tensors="""pt"""				, )
							lowercase__					      =		text_inputs.input_ids

							if text_input_ids.shape[-1] > self.tokenizer.model_max_length:
									lowercase__					      =		self.tokenizer.batch_decode(text_input_ids[:, self.tokenizer.model_max_length :]		)
									logger.warning(
									    """The following part of your input was truncated because CLIP can only handle sequences up to"""
									    f''' {self.tokenizer.model_max_length} tokens: {removed_text}'''		)
									lowercase__					      =		text_input_ids[:, : self.tokenizer.model_max_length]

							if text_embeddings is None:
									lowercase__					      =		self.text_encoder(text_input_ids.to(self.device		)		)[0]

							# duplicate text embeddings for each generation per prompt, using mps friendly method
							lowercase__      ,     lowercase__      ,     lowercase__					      =		text_embeddings.shape
							lowercase__					      =		text_embeddings.repeat(1				, _UpperCAmelCase				, 1		)
							lowercase__					      =		text_embeddings.view(bs_embed * num_images_per_prompt				, _UpperCAmelCase				, -1		)

							# here `guidance_scale` is defined analog to the guidance weight `w` of equation (2)
							# of the Imagen paper: https://arxiv.org/pdf/2205.11487.pdf . `guidance_scale = 1`
							# corresponds to doing no classifier free guidance.
							lowercase__					      =		guidance_scale > 1.0
							# get unconditional embeddings for classifier free guidance
							if do_classifier_free_guidance:
									lowercase__					      =		42
									if negative_prompt is None:
											lowercase__					      =		[""""""]
									elif type(_UpperCAmelCase		) is not type(_UpperCAmelCase		):
											raise TypeError(
											    f'''`negative_prompt` should be the same type to `prompt`, but got {type(_UpperCAmelCase		)} !='''
											    f''' {type(_UpperCAmelCase		)}.'''		)
									elif isinstance(_UpperCAmelCase				, _UpperCAmelCase		):
											lowercase__					      =		[negative_prompt]
									elif batch_size != len(_UpperCAmelCase		):
											raise ValueError(
											    f'''`negative_prompt`: {negative_prompt} has batch size {len(_UpperCAmelCase		)}, but `prompt`:'''
											    f''' {prompt} has batch size {batch_size}. Please make sure that passed `negative_prompt` matches'''
											    """ the batch size of `prompt`."""		)
									else:
											lowercase__					      =		negative_prompt

									lowercase__					      =		text_input_ids.shape[-1]
									lowercase__					      =		self.tokenizer(
									    _UpperCAmelCase				, padding="""max_length"""				, max_length=_UpperCAmelCase				, truncation=_UpperCAmelCase				, return_tensors="""pt"""				, )
									lowercase__					      =		self.text_encoder(uncond_input.input_ids.to(self.device		)		)[0]

									# duplicate unconditional embeddings for each generation per prompt, using mps friendly method
									lowercase__					      =		uncond_embeddings.shape[1]
									lowercase__					      =		uncond_embeddings.repeat(_UpperCAmelCase				, _UpperCAmelCase				, 1		)
									lowercase__					      =		uncond_embeddings.view(batch_size * num_images_per_prompt				, _UpperCAmelCase				, -1		)

									# For classifier free guidance, we need to do two forward passes.
									# Here we concatenate the unconditional and text embeddings into a single batch
									# to avoid doing two forward passes
									lowercase__					      =		torch.cat([uncond_embeddings, text_embeddings]		)

							# get the initial random noise unless the user supplied it

							# Unlike in other pipelines, latents need to be generated in the target device
							# for 1-to-1 results reproducibility with the CompVis implementation.
							# However this currently doesn't work in `mps`.
							lowercase__					      =		(batch_size * num_images_per_prompt, self.unet.config.in_channels, height // 8, width // 8)
							lowercase__					      =		(batch_size * num_images_per_prompt, self.unet.config.in_channels, 64, 64)
							lowercase__					      =		text_embeddings.dtype
							if latents is None:
									if self.device.type == "mps":
											# randn does not exist on mps
											lowercase__					      =		torch.randn(
											    _UpperCAmelCase				, generator=_UpperCAmelCase				, device="""cpu"""				, dtype=_UpperCAmelCase		).to(self.device		)
											lowercase__					      =		torch.randn(_UpperCAmelCase				, generator=_UpperCAmelCase				, device="""cpu"""				, dtype=_UpperCAmelCase		).to(
											    self.device		)
									else:
											lowercase__					      =		torch.randn(
											    _UpperCAmelCase				, generator=_UpperCAmelCase				, device=self.device				, dtype=_UpperCAmelCase		)
											lowercase__					      =		torch.randn(_UpperCAmelCase				, generator=_UpperCAmelCase				, device=self.device				, dtype=_UpperCAmelCase		)
							else:
									if latents_reference.shape != latents_shape:
											raise ValueError(f'''Unexpected latents shape, got {latents.shape}, expected {latents_shape}'''		)
									lowercase__					      =		latents_reference.to(self.device		)
									lowercase__					      =		latents.to(self.device		)

							# This is the key part of the pipeline where we
							# try to ensure that the generated images w/ the same seed
							# but different sizes actually result in similar images
							lowercase__					      =		(latents_shape[3] - latents_shape_reference[3]) // 2
							lowercase__					      =		(latents_shape[2] - latents_shape_reference[2]) // 2
							lowercase__					      =		latents_shape_reference[3] if dx >= 0 else latents_shape_reference[3] + 2 * dx
							lowercase__					      =		latents_shape_reference[2] if dy >= 0 else latents_shape_reference[2] + 2 * dy
							lowercase__					      =		0 if dx < 0 else dx
							lowercase__					      =		0 if dy < 0 else dy
							lowercase__					      =		max(-dx				, 0		)
							lowercase__					      =		max(-dy				, 0		)
							# import pdb
							# pdb.set_trace()
							lowercase__					      =		latents_reference[:, :, dy : dy + h, dx : dx + w]

							# set timesteps
							self.scheduler.set_timesteps(_UpperCAmelCase		)

							# Some schedulers like PNDM have timesteps as arrays
							# It's more optimized to move all timesteps to correct device beforehand
							lowercase__					      =		self.scheduler.timesteps.to(self.device		)

							# scale the initial noise by the standard deviation required by the scheduler
							lowercase__					      =		latents * self.scheduler.init_noise_sigma

							# prepare extra kwargs for the scheduler step, since not all schedulers have the same signature
							# eta (η) is only used with the DDIMScheduler, it will be ignored for other schedulers.
							# eta corresponds to η in DDIM paper: https://arxiv.org/abs/2010.02502
							# and should be between [0, 1]
							lowercase__					      =		"""eta""" in set(inspect.signature(self.scheduler.step		).parameters.keys()		)
							lowercase__					      =		{}
							if accepts_eta:
									lowercase__					      =		eta

							for i, t in enumerate(self.progress_bar(_UpperCAmelCase		)		):
									# expand the latents if we are doing classifier free guidance
									lowercase__					      =		torch.cat([latents] * 2		) if do_classifier_free_guidance else latents
									lowercase__					      =		self.scheduler.scale_model_input(_UpperCAmelCase				, _UpperCAmelCase		)

									# predict the noise residual
									lowercase__					      =		self.unet(_UpperCAmelCase				, _UpperCAmelCase				, encoder_hidden_states=_UpperCAmelCase		).sample

									# perform guidance
									if do_classifier_free_guidance:
											lowercase__      ,     lowercase__					      =		noise_pred.chunk(2		)
											lowercase__					      =		noise_pred_uncond + guidance_scale * (noise_pred_text - noise_pred_uncond)

									# compute the previous noisy sample x_t -> x_t-1
									lowercase__					      =		self.scheduler.step(_UpperCAmelCase				, _UpperCAmelCase				, _UpperCAmelCase				, **_UpperCAmelCase		).prev_sample

									# call the callback, if provided
									if callback is not None and i % callback_steps == 0:
											callback(_UpperCAmelCase				, _UpperCAmelCase				, _UpperCAmelCase		)

							lowercase__					      =		1 / 0.18_215 * latents
							lowercase__					      =		self.vae.decode(_UpperCAmelCase		).sample

							lowercase__					      =		(image / 2 + 0.5).clamp(0				, 1		)

							# we always cast to float32 as this does not cause significant overhead and is compatible with bfloat16
							lowercase__					      =		image.cpu().permute(0				, 2				, 3				, 1		).float().numpy()

							if self.safety_checker is not None:
									lowercase__					      =		self.feature_extractor(self.numpy_to_pil(_UpperCAmelCase		)				, return_tensors="""pt"""		).to(
									    self.device		)
									lowercase__      ,     lowercase__					      =		self.safety_checker(
									    images=_UpperCAmelCase				, clip_input=safety_checker_input.pixel_values.to(text_embeddings.dtype		)		)
							else:
									lowercase__					      =		None

							if output_type == "pil":
									lowercase__					      =		self.numpy_to_pil(_UpperCAmelCase		)

							if not return_dict:
									return (image, has_nsfw_concept)

							return StableDiffusionPipelineOutput(images=_UpperCAmelCase				, nsfw_content_detected=_UpperCAmelCase		)

