



"""simple docstring"""







import argparse
import collections

import jax
import jax.numpy as jnp
import torch
import torch.nn as nn
from clip.model import CLIP
from flax.training import checkpoints
from huggingface_hub import Repository

from transformers import (
    CLIPTokenizer,
    OwlViTConfig,
    OwlViTForObjectDetection,
    OwlViTImageProcessor,
    OwlViTModel,
    OwlViTProcessor,
)


UpperCAmelCase:   Tuple       			=							{
    """vit_b32""": {
        """embed_dim""": 512,
        """image_resolution""": 768,
        """context_length""": 16,
        """vocab_size""": 49_408,
        """vision_layers""": 12,
        """vision_width""": 768,
        """vision_patch_size""": 32,
        """transformer_width""": 512,
        """transformer_heads""": 8,
        """transformer_layers""": 12,
    },
    """vit_b16""": {
        """embed_dim""": 512,
        """image_resolution""": 768,
        """context_length""": 16,
        """vocab_size""": 49_408,
        """vision_layers""": 12,
        """vision_width""": 768,
        """vision_patch_size""": 16,
        """transformer_width""": 512,
        """transformer_heads""": 8,
        """transformer_layers""": 12,
    },
    """vit_l14""": {
        """embed_dim""": 768,
        """image_resolution""": 840,
        """context_length""": 16,
        """vocab_size""": 49_408,
        """vision_layers""": 24,
        """vision_width""": 1_024,
        """vision_patch_size""": 14,
        """transformer_width""": 768,
        """transformer_heads""": 12,
        """transformer_layers""": 12,
    },
}


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase=""					,							__UpperCAmelCase="/"	):
		_lowercase							:      str			=					[]

		for k, v in params.items():
				_lowercase							:      Any			=					parent_key + sep + k if parent_key else k

				if isinstance(__UpperCAmelCase					,							collections.MutableMapping	):
						items.extend(flatten_nested_dict(__UpperCAmelCase					,							__UpperCAmelCase					,							sep=__UpperCAmelCase	).items()	)
				else:
						items.append((new_key, v)	)
		return dict(__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase	):
		return jax.tree_util.tree_map(lambda __UpperCAmelCase	: x.astype(jnp.floataa	) if x.dtype == jnp.bfloataa else x					,							__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		_lowercase			,   _lowercase			,   _lowercase							:      Dict			=					pt_attn_layer.in_proj_weight.chunk(3					,							dim=0	)
		_lowercase			,   _lowercase			,   _lowercase							:      Optional[Any]			=					pt_attn_layer.in_proj_bias.chunk(3					,							dim=0	)

		_lowercase							:      Optional[int]			=					pt_attn_layer.out_proj.weight
		_lowercase							:      str			=					pt_attn_layer.out_proj.bias

		_lowercase							:      Optional[Any]			=					q_proj
		_lowercase							:      int			=					q_proj_bias

		_lowercase							:      List[str]			=					k_proj
		_lowercase							:      Optional[int]			=					k_proj_bias

		_lowercase							:      Optional[Any]			=					v_proj
		_lowercase							:      Optional[Any]			=					v_proj_bias

		_lowercase							:      Union[str, Any]			=					out_proj_weights
		_lowercase							:      List[str]			=					out_proj_bias


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		copy_linear(hf_mlp.fca					,							pt_mlp.c_fc	)
		copy_linear(hf_mlp.fca					,							pt_mlp.c_proj	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		_lowercase							:      str			=					pt_linear.weight
		_lowercase							:      List[str]			=					pt_linear.bias


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		# copy layer norms
		copy_linear(hf_layer.layer_norma					,							pt_layer.ln_a	)
		copy_linear(hf_layer.layer_norma					,							pt_layer.ln_a	)

		# copy MLP
		copy_mlp(hf_layer.mlp					,							pt_layer.mlp	)

		# copy attn
		copy_attn_layer(hf_layer.self_attn					,							pt_layer.attn	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		for hf_layer, pt_layer in zip(__UpperCAmelCase					,							__UpperCAmelCase	):
				copy_layer(__UpperCAmelCase					,							__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		# copy  embeds
		_lowercase							:      Any			=					pt_model.token_embedding.weight
		_lowercase							:      List[str]			=					pt_model.positional_embedding

		# copy layer norm
		copy_linear(hf_encoder.final_layer_norm					,							pt_model.ln_final	)

		# copy hidden layers
		copy_layers(hf_encoder.encoder.layers					,							pt_model.transformer.resblocks	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		# copy projection
		_lowercase							:      Dict			=					pt_model.text_projection.data.T

		# copy text encoder
		copy_encoder(hf_model.text_model					,							__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		# copy projection
		_lowercase							:      Any			=					pt_model.visual.proj.data.T

		# copy layer norms
		copy_linear(hf_model.vision_model.pre_layernorm					,							pt_model.visual.ln_pre	)
		copy_linear(hf_model.vision_model.post_layernorm					,							pt_model.visual.ln_post	)

		# copy embeds
		_lowercase							:      List[str]			=					pt_model.visual.conva.weight.data
		_lowercase							:      Any			=					pt_model.visual.class_embedding
		_lowercase							:      List[Any]			=					pt_model.visual.positional_embedding.data

		# copy encoder
		copy_layers(hf_model.vision_model.encoder.layers					,							pt_model.visual.transformer.resblocks	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		_lowercase							:      Tuple			=					flatten_nested_dict(flax_params["""backbone"""]["""merged_class_token"""]	)

		_lowercase							:      str			=					torch.from_numpy(flax_class_token_params["""scale"""]	)
		_lowercase							:      List[str]			=					torch.from_numpy(flax_class_token_params["""bias"""]	)
		_lowercase							:      List[Any]			=					nn.Parameter(__UpperCAmelCase	)
		_lowercase							:      Any			=					nn.Parameter(__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		_lowercase							:      Any			=					hf_model.state_dict()
		_lowercase							:      Optional[int]			=					{}

		# Rename class prediction head flax params to pytorch HF
		_lowercase							:      str			=					flatten_nested_dict(flax_params["""class_head"""]	)

		for flax_key, v in flax_class_params.items():
				_lowercase							:      Optional[Any]			=					flax_key.replace("""/"""					,							"""."""	)
				_lowercase							:      int			=					torch_key.replace(""".kernel"""					,							""".weight"""	)
				_lowercase							:      int			=					torch_key.replace("""Dense_0"""					,							"""dense0"""	)
				_lowercase							:      int			=					"""class_head.""" + torch_key

				if "weight" in torch_key and v.ndim == 2:
						_lowercase							:      str			=					v.T

				_lowercase							:      List[str]			=					nn.Parameter(torch.from_numpy(__UpperCAmelCase	)	)

		# Rename box prediction box flax params to pytorch HF
		_lowercase							:      Union[str, Any]			=					flatten_nested_dict(flax_params["""obj_box_head"""]	)

		for flax_key, v in flax_box_params.items():
				_lowercase							:      Union[str, Any]			=					flax_key.replace("""/"""					,							"""."""	)
				_lowercase							:      Optional[int]			=					torch_key.replace(""".kernel"""					,							""".weight"""	)
				_lowercase							:      List[str]			=					torch_key.replace("""_"""					,							""""""	).lower()
				_lowercase							:      Any			=					"""box_head.""" + torch_key

				if "weight" in torch_key and v.ndim == 2:
						_lowercase							:      int			=					v.T

				_lowercase							:      Tuple			=					nn.Parameter(torch.from_numpy(__UpperCAmelCase	)	)

		# Copy flax params to PyTorch params
		for name, param in new_params.items():
				if name in pt_params.keys():
						pt_params[name].copy_(__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		for k, v in flax_attn_params.items():
				if k.startswith("""transformer"""	):
						_lowercase							:      Tuple			=					k.replace("""transformer.resblocks"""					,							"""text_model.encoder.layers"""	)
				else:
						_lowercase							:      Tuple			=					k.replace("""visual.transformer.resblocks"""					,							"""vision_model.encoder.layers"""	)

				_lowercase							:      str			=					torch_key.replace("""attn"""					,							"""self_attn"""	)
				_lowercase							:      Dict			=					torch_key.replace("""key"""					,							"""k_proj"""	)
				_lowercase							:      Union[str, Any]			=					torch_key.replace("""value"""					,							"""v_proj"""	)
				_lowercase							:      Optional[int]			=					torch_key.replace("""query"""					,							"""q_proj"""	)
				_lowercase							:      Optional[Any]			=					torch_key.replace("""out"""					,							"""out_proj"""	)

				if "bias" in torch_key and v.ndim == 2:
						_lowercase							:      Any			=					v.shape[0] * v.shape[1]
						_lowercase							:      Tuple			=					v.reshape(__UpperCAmelCase	)

				if "weight" in torch_key and "out" in torch_key:
						_lowercase							:      Union[str, Any]			=					(v.shape[0] * v.shape[1], v.shape[2])
						_lowercase							:      Any			=					v.reshape(__UpperCAmelCase	).T

				if "weight" in torch_key and "out" not in torch_key:
						_lowercase							:      Tuple			=					(v.shape[0], v.shape[1] * v.shape[2])
						_lowercase							:      str			=					v.reshape(__UpperCAmelCase	).T

				# Copy flax CLIP attn params to HF PyTorch params
				_lowercase							:      Dict			=					torch.from_numpy(__UpperCAmelCase	)
				hf_backbone.state_dict()[torch_key].copy_(__UpperCAmelCase	)


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase	):
		_lowercase							:      List[str]			=					{}
		_lowercase							:      int			=					[]

		for k, v in params.items():
				if "attn." in k:
						_lowercase							:      Any			=					k[: k.rindex("""attn."""	) + 5]
						if base in processed_attn_layers:
								continue

						processed_attn_layers.append(__UpperCAmelCase	)
						_lowercase							:      Union[str, Any]			=					params[base + """out.weight"""].shape[-1]
						_lowercase							:      Any			=					params[base + """out.weight"""].reshape(__UpperCAmelCase					,							__UpperCAmelCase	).T
						_lowercase							:      str			=					params[base + """out.bias"""]
				else:
						_lowercase							:      Tuple			=					v
		return new_params


def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase	):
		_lowercase							:      Any			=					CLIP(**__UpperCAmelCase	)
		torch_model.eval()
		_lowercase							:      Union[str, Any]			=					torch_model.state_dict()

		_lowercase							:      Optional[Any]			=					flatten_nested_dict(flax_params["""backbone"""]["""clip"""]	)
		_lowercase							:      List[Any]			=					{}

		for flax_key, v in flax_clip_params.items():
				_lowercase							:      Union[str, Any]			=					flax_key.replace("""/"""					,							"""."""	)
				_lowercase							:      Dict			=					torch_key.replace("""text.token_embedding.embedding"""					,							"""token_embedding.kernel"""	)

				if (
				    torch_key.startswith("""text.transformer"""	)
				    or torch_key.startswith("""text.text_projection"""	)
				    or torch_key.startswith("""text.ln_final"""	)
				    or torch_key.startswith("""text.positional_embedding"""	)
				):
						_lowercase							:      Union[str, Any]			=					torch_key[5:]

				_lowercase							:      Tuple			=					torch_key.replace("""text_projection.kernel"""					,							"""text_projection"""	)
				_lowercase							:      List[str]			=					torch_key.replace("""visual.proj.kernel"""					,							"""visual.proj"""	)
				_lowercase							:      List[Any]			=					torch_key.replace(""".scale"""					,							""".weight"""	)
				_lowercase							:      Optional[Any]			=					torch_key.replace(""".kernel"""					,							""".weight"""	)

				if "conv" in torch_key or "downsample.0.weight" in torch_key:
						_lowercase							:      Tuple			=					v.transpose(3					,							2					,							0					,							1	)

				elif "weight" in torch_key and v.ndim == 2 and "embedding" not in torch_key:
						# Fully connected layers are transposed, embeddings are not
						_lowercase							:      int			=					v.T

				_lowercase							:      Optional[int]			=					v

		_lowercase							:      List[Any]			=					_convert_attn_layers(__UpperCAmelCase	)
		new_torch_params.update(__UpperCAmelCase	)
		_lowercase							:      Union[str, Any]			=					{}

		# Copy flax CLIP backbone params to PyTorch params
		for name, param in new_torch_params.items():
				if name in torch_clip_params.keys():
						_lowercase							:      Optional[int]			=					torch.from_numpy(new_torch_params[name]	)
						torch_clip_params[name].copy_(__UpperCAmelCase	)
				else:
						_lowercase							:      Union[str, Any]			=					param

		return torch_clip_params, torch_model, attn_params


@torch.no_grad()
def       __SCREAMING_SNAKE_CASE      (   __UpperCAmelCase					,							__UpperCAmelCase					,							__UpperCAmelCase					,							__UpperCAmelCase					,							__UpperCAmelCase=None	):
		_lowercase							:      Any			=					Repository(__UpperCAmelCase					,							clone_from=F"""google/{pytorch_dump_folder_path}"""	)
		repo.git_pull()

		if config_path is not None:
				_lowercase							:      Optional[int]			=					OwlViTConfig.from_pretrained(__UpperCAmelCase	)
		else:
				_lowercase							:      List[str]			=					OwlViTConfig()

		_lowercase							:      List[str]			=					OwlViTModel(__UpperCAmelCase	).eval()
		_lowercase							:      Tuple			=					OwlViTForObjectDetection(__UpperCAmelCase	).eval()

		copy_text_model_and_projection(__UpperCAmelCase					,							__UpperCAmelCase	)
		copy_vision_model_and_projection(__UpperCAmelCase					,							__UpperCAmelCase	)
		_lowercase							:      Optional[int]			=					pt_backbone.logit_scale
		copy_flax_attn_params(__UpperCAmelCase					,							__UpperCAmelCase	)

		_lowercase							:      List[str]			=					hf_backbone
		copy_class_merge_token(__UpperCAmelCase					,							__UpperCAmelCase	)
		copy_class_box_heads(__UpperCAmelCase					,							__UpperCAmelCase	)

		# Save HF model
		hf_model.save_pretrained(repo.local_dir	)

		# Initialize image processor
		_lowercase							:      Union[str, Any]			=					OwlViTImageProcessor(
		    size=config.vision_config.image_size					,							crop_size=config.vision_config.image_size	)
		# Initialize tokenizer
		_lowercase							:      Dict			=					CLIPTokenizer.from_pretrained("""openai/clip-vit-base-patch32"""					,							pad_token="""!"""					,							model_max_length=16	)

		# Initialize processor
		_lowercase							:      str			=					OwlViTProcessor(image_processor=__UpperCAmelCase					,							tokenizer=__UpperCAmelCase	)
		image_processor.save_pretrained(repo.local_dir	)
		processor.save_pretrained(repo.local_dir	)

		repo.git_add()
		repo.git_commit("""Upload model and processor"""	)
		repo.git_push()


if __name__ == "__main__":
		UpperCAmelCase:   str       			=							argparse.ArgumentParser()
		# Required parameters
		parser.add_argument(
		    """--owlvit_version""",
		    default=None,
		    type=str,
		    required=True,
		    help="""OWL-ViT model name [clip_b16, clip_b32, clip_l14].""",
		)
		parser.add_argument(
		    """--owlvit_checkpoint""", default=None, type=str, required=True, help="""Path to flax model checkpoint."""
		)
		parser.add_argument("""--hf_config""", default=None, type=str, required=True, help="""Path to HF model config.""")
		parser.add_argument(
		    """--pytorch_dump_folder_path""", default="""hf_model""", type=str, help="""Path to the output PyTorch model."""
		)
		UpperCAmelCase:   int       			=							parser.parse_args()

		# Initialize PyToch clip model
		UpperCAmelCase:   Dict       			=							args.owlvit_version
		if model_name == "clip_b16":
				UpperCAmelCase:   Union[str, Any]       			=							CONFIGS["""vit_b16"""]
		elif model_name == "clip_b32":
				UpperCAmelCase:   Any       			=							CONFIGS["""vit_b32"""]
		elif model_name == "clip_l14":
				UpperCAmelCase:   str       			=							CONFIGS["""vit_l14"""]

		# Load from checkpoint and convert params to float-32
		UpperCAmelCase:   List[str]       			=							checkpoints.restore_checkpoint(args.owlvit_checkpoint, target=None)["""optimizer"""]["""target"""]
		UpperCAmelCase:   Dict       			=							jax.tree_util.tree_map(lambda x: x.astype(jnp.floataa) if x.dtype == jnp.bfloataa else x, variables)
		del variables

		# Convert CLIP backbone
		UpperCAmelCase			,      UpperCAmelCase			,      UpperCAmelCase:   str       			=							convert_clip_backbone(flax_params, torch_config)

		convert_owlvit_checkpoint(clip_pt, flax_params, attn_params, args.pytorch_dump_folder_path, args.hf_config)







'''simple docstring'''






from __future__ import annotations

from collections.abc import Callable
from typing import Generic, TypeVar

__UpperCAmelCase    				=							TypeVar('''T''')
__UpperCAmelCase    				=							TypeVar('''U''')




class 		a__				(							Generic[T, U]  ):





							'''simple docstring'''






							def __init__(		self					,    lowerCamelCase_					,    lowerCamelCase_   )       ->							Union[str, Any]:
														lowerCAmelCase__          =	key
														lowerCAmelCase__          =	val
														lowerCAmelCase__          =	0
														lowerCAmelCase__          =	None
														lowerCAmelCase__          =	None







							def __repr__(		self   )       ->							str:
														return "Node: key: {}, val: {}, freq: {}, has next: {}, has prev: {}".format(
														    self.key					,    self.val					,    self.freq					,    self.next is not None					,    self.prev is not None   )




class 		a__				(							Generic[T, U]  ):





							'''simple docstring'''






							def __init__(		self   )       ->							None:
														lowerCAmelCase__          =	DoubleLinkedListNode(lowerCamelCase_					,    lowerCamelCase_   )
														lowerCAmelCase__          =	DoubleLinkedListNode(lowerCamelCase_					,    lowerCamelCase_   )
														lowerCAmelCase__   ,					lowerCAmelCase__          =	self.rear, self.head







							def __repr__(		self   )       ->							str:
														lowerCAmelCase__          =	['''DoubleLinkedList''']
														lowerCAmelCase__          =	self.head
														while node.next is not None:
																					rep.append(str(lowerCamelCase_   )   )
																					lowerCAmelCase__          =	node.next
														rep.append(str(self.rear   )   )
														return ",\n    ".join(lowerCamelCase_   )







							def   __SCREAMING_SNAKE_CASE							(		self					,    lowerCamelCase_   )       ->							None:
														lowerCAmelCase__          =	self.rear.prev

														# All nodes other than self.head are guaranteed to have non-None previous
														assert previous is not None

														lowerCAmelCase__          =	node
														lowerCAmelCase__          =	previous
														lowerCAmelCase__          =	node
														lowerCAmelCase__          =	self.rear
														node.freq += 1
														self._position_node(lowerCamelCase_   )







							def   __SCREAMING_SNAKE_CASE							(		self					,    lowerCamelCase_   )       ->							None:

														while node.prev is not None and node.prev.freq > node.freq:
																					# swap node with previous node
																					lowerCAmelCase__          =	node.prev

																					lowerCAmelCase__          =	previous_node.prev
																					lowerCAmelCase__          =	node.prev
																					lowerCAmelCase__          =	previous_node
																					lowerCAmelCase__          =	node







							def   __SCREAMING_SNAKE_CASE							(		self					,    lowerCamelCase_   )       ->							DoubleLinkedListNode[T, U] | None:

														if node.prev is None or node.next is None:
																					return None

														lowerCAmelCase__          =	node.next
														lowerCAmelCase__          =	node.prev
														lowerCAmelCase__          =	None
														lowerCAmelCase__          =	None
														return node




class 		a__				(							Generic[T, U]  ):





							'''simple docstring'''






							lowercase__			:   dict[Callable[[T], U], LFUCache[T, U]]							=      {}







							def __init__(		self					,    lowerCamelCase_   )       ->							Any:
														lowerCAmelCase__          =	DoubleLinkedList()
														lowerCAmelCase__          =	capacity
														lowerCAmelCase__          =	0
														lowerCAmelCase__          =	0
														lowerCAmelCase__          =	0
														lowerCAmelCase__          =	{}







							def __repr__(		self   )       ->							str:
														return (
														    F"""CacheInfo(hits={self.hits}, misses={self.miss}, """
														    F"""capacity={self.capacity}, current_size={self.num_keys})"""
														)







							def __contains__(		self					,    lowerCamelCase_   )       ->							bool:
														return key in self.cache







							def   __SCREAMING_SNAKE_CASE							(		self					,    lowerCamelCase_   )       ->							U | None:

														if key in self.cache:
																					self.hits += 1
																					lowerCAmelCase__          =	self.cache[key]
																					lowerCAmelCase__          =	self.list.remove(self.cache[key]   )
																					assert node == value_node

																					# node is guaranteed not None because it is in self.cache
																					assert node is not None
																					self.list.add(lowerCamelCase_   )
																					return node.val
														self.miss += 1
														return None







							def   __SCREAMING_SNAKE_CASE							(		self					,    lowerCamelCase_					,    lowerCamelCase_   )       ->							None:

														if key not in self.cache:
																					if self.num_keys >= self.capacity:
																												# delete first node when over capacity
																												lowerCAmelCase__          =	self.list.head.next

																												# guaranteed to have a non-None first node when num_keys > 0
																												# explain to type checker via assertions
																												assert first_node is not None
																												assert first_node.key is not None
																												assert self.list.remove(lowerCamelCase_   ) is not None
																												# first_node guaranteed to be in list

																												del self.cache[first_node.key]
																												self.num_keys -= 1
																					lowerCAmelCase__          =	DoubleLinkedListNode(lowerCamelCase_					,    lowerCamelCase_   )
																					self.list.add(self.cache[key]   )
																					self.num_keys += 1

														else:
																					lowerCAmelCase__          =	self.list.remove(self.cache[key]   )
																					assert node is not None  # node guaranteed to be in list
																					lowerCAmelCase__          =	value
																					self.list.add(lowerCamelCase_   )







							@classmethod
							def   __SCREAMING_SNAKE_CASE							(		cls					,    lowerCamelCase_ = 1_28   )       ->							Callable[[Callable[[T], U]], Callable[..., U]]:

														def cache_decorator_inner(lowerCamelCase_   ) -> Callable[..., U]:
																					def cache_decorator_wrapper(*lowerCamelCase_   ) -> U:
																												if func not in cls.decorator_function_to_instance_map:
																																			lowerCAmelCase__          =	LFUCache(lowerCamelCase_   )

																												lowerCAmelCase__          =	cls.decorator_function_to_instance_map[func].get(args[0]   )
																												if result is None:
																																			lowerCAmelCase__          =	func(*lowerCamelCase_   )
																																			cls.decorator_function_to_instance_map[func].put(args[0]					,    lowerCamelCase_   )
																												return result

																					def cache_info() -> LFUCache[T, U]:
																												return cls.decorator_function_to_instance_map[func]

																					setattr(lowerCamelCase_					,    '''cache_info'''					,    lowerCamelCase_   )  # noqa: B010

																					return cache_decorator_wrapper

														return cache_decorator_inner


if __name__ == "__main__":
		import doctest

		doctest.testmod()