
    j,                        d dl mZ d dlmZ d dlmZmZmZmZm	Z	m
Z
mZ dddededed	e	e         d
ef
dZddddededed	e	e         de	e         d
efdZ	 	 	 ddeded	e	e         de	eee
eef                  eeef         f                  d
eee
eef                  eeef         f         f
dZdeee
eef                  eeef         f         d
efdZddZddZdS )    )defaultdict)zip_longest)AnyCallableDictListOptionalTupleUnionN)is_leaffntreerestr   returnc                     |          r	  |gR  S t          |t          t          f          rLt          |          } fdt	          |          D             }t          |d          r || n
 ||          S t          |t                    r" fd|                                D             S   |gR  S )a  Applies ``fn`` to the leaves of the Python tree ``tree`` and
    returns a new collection with the results.

    If ``rest`` is provided, every item is assumed to be a superset of ``tree``
    and the corresponding leaves are provided as extra positional arguments to
    ``fn``. In that respect, :meth:`tree_map` is closer to :func:`itertools.starmap`
    than to :func:`map`.

    The keyword argument ``is_leaf`` decides what constitutes a leaf from
    ``tree`` similar to :func:`tree_flatten`.

    .. code-block:: python

        import mlx.nn as nn
        from mlx.utils import tree_map

        model = nn.Linear(10, 10)
        print(model.parameters().keys())
        # dict_keys(['weight', 'bias'])

        # square the parameters
        model.update(tree_map(lambda x: x*x, model.parameters()))

    Args:
        fn (callable): The function that processes the leaves of the tree.
        tree (Any): The main Python tree that will be iterated upon.
        rest (tuple[Any]): Extra trees to be iterated together with ``tree``.
        is_leaf (callable, optional): An optional callable that returns ``True``
           if the passed object is considered a leaf or ``False`` otherwise.

    Returns:
        A Python tree with the new values returned by ``fn``.
    Nc              3   Z   K   | ]$\  }t          |gfd D             R diV  %dS )c              3   (   K   | ]}|         V  d S N .0ris     S/lsinfo/ai/hellotax_ai/base_platform/venv/lib/python3.11/site-packages/mlx/utils.py	<genexpr>z%tree_map.<locals>.<genexpr>.<genexpr>0   s'      !5!51!A$!5!5!5!5!5!5    r   Ntree_map)r   childr   r   r   r   s     @r   r   ztree_map.<locals>.<genexpr>/   sk       
 
5 RG!5!5!5!5!5!5!5GGGwGG
 
 
 
 
 
r   _fieldsc                 T    i | ]#\  }t          |gfd D             R di$S )c              3   (   K   | ]}|         V  d S r   r   r   r   ks     r   r   z&tree_map.<locals>.<dictcomp>.<genexpr>6   s'      $8$8aQqT$8$8$8$8$8$8r   r   r   )r   r   r$   r   r   r   s     @r   
<dictcomp>ztree_map.<locals>.<dictcomp>5   s_     
 
 
5 xEJ$8$8$8$84$8$8$8JJJ'JJ
 
 
r   )
isinstancelisttupletype	enumeratehasattrdictitems)r   r   r   r   TreeTypesubtreess   ` ``  r   r   r      s   H wwt}}r$	D4-	(	( ::
 
 
 
 
 
%dOO
 
 
 '.dI&>&>Vxx""HHXDVDVV	D$		 
 
 
 
 
 
 JJLL
 
 
 	

 r$r   r   pathr1   c                     |          r
  ||gR  S t          |t          t          f          r?|r| dndt          |          } | fdt	          |          D                       S t          |t
                    r,|r| dnd fd|                                D             S   ||gR  S )a7  Applies ``fn`` to the path and leaves of the Python tree ``tree`` and
    returns a new collection with the results.

    This function is the same :func:`tree_map` but the ``fn`` takes the path as
    the first argument followed by the remaining tree nodes.

    Args:
        fn (callable): The function that processes the leaves of the tree.
        tree (Any): The main Python tree that will be iterated upon.
        rest (tuple[Any]): Extra trees to be iterated together with ``tree``.
        is_leaf (Optional[Callable]): An optional callable that returns ``True``
           if the passed object is considered a leaf or ``False`` otherwise.
        path (Optional[Any]): Prefix will be added to the result.

    Returns:
        A Python tree with the new values returned by ``fn``.

    Example:
        >>> from mlx.utils import tree_map_with_path
        >>> tree = {"model": [{"w": 0, "b": 1}, {"w": 0, "b": 1}]}
        >>> new_tree = tree_map_with_path(lambda path, _: print(path), tree)
        model.0.w
        model.0.b
        model.1.w
        model.1.b
    N. c              3   d   K   | ])\  }t          |gfd D             R   dV  *dS )c              3   (   K   | ]}|         V  d S r   r   r   s     r   r   z/tree_map_with_path.<locals>.<genexpr>.<genexpr>e   '      00aQqT000000r   r0   Ntree_map_with_path)r   r   r   r   r   prefixr   s     @r   r   z%tree_map_with_path.<locals>.<genexpr>c   s       
 
 5 E00004000 :A6ST  
 
 
 
 
 
r   c           	      ^    i | ](\  }t          |gfd D             R   d)S )c              3   (   K   | ]}|         V  d S r   r   r#   s     r   r   z0tree_map_with_path.<locals>.<dictcomp>.<genexpr>m   r7   r   r0   r8   )r   r   r$   r   r   r:   r   s     @r   r%   z&tree_map_with_path.<locals>.<dictcomp>k   s{     
 
 
 5 !E00004000 :A6ST  
 
 
r   )r&   r'   r(   r)   r*   r,   r-   )r   r   r   r1   r   r.   r:   s   ` ` ` @r   r9   r9   =   sD   B wwt}}r$$t$$$$	D4-	(	( %#+D::x 
 
 
 
 
 
 
 &dOO	
 
 
 
 
 	
 
D$		 	%#+D
 
 
 
 
 
 
 !JJLL	
 
 
 	
 r$$t$$$$r   r4   r:   destinationc                 :   |g }t          |t                    r|j        }n,t          |t                    r|j        }nt          d          |# ||           r ||dd         | fg           |S t          | t          t          f          r.t          |           D ]\  }}t          || d| ||           |S t          | t                    r3| 	                                D ]\  }}t          || d| ||           |S  ||dd         | fg           |S )an  Flattens a Python tree to a list of key, value tuples.

    The keys are using the dot notation to define trees of arbitrary depth and
    complexity.

    .. code-block:: python

        from mlx.utils import tree_flatten

        print(tree_flatten([[[0]]]))
        # [("0.0.0", 0)]

        print(tree_flatten([[[0]]], prefix=".hello"))
        # [("hello.0.0.0", 0)]

        tree_flatten({"a": {"b": 1}}, destination={})
        {"a.b": 1}

    .. note::
       Dictionaries should have keys that are valid Python identifiers.

    Args:
        tree (Any): The Python tree to be flattened.
        prefix (str): A prefix to use for the keys. The first character is
            always discarded.
        is_leaf (callable): An optional callable that returns True if the
            passed object is considered a leaf or False otherwise.
        destination (list or dict, optional): A list or dictionary to store the
            flattened tree. If None an empty list will be used. Default: ``None``.

    Returns:
        Union[List[Tuple[str, Any]], Dict[str, Any]]: The flat representation of
            the Python tree.
    Nz;Destination should be either a list or a dictionary or None   r3   )
r&   r'   extendr,   update
ValueErrorr(   r*   tree_flattenr-   )	r   r:   r   r=   _add_to_destinationr   itemkeyvalues	            r   rC   rC   u   sz   P 
 +t$$ X)0	K	&	& X)0VWWW wwt}}fQRRj$/0111 $u&&   	F 	FGAt&1EEEE $ **,, 	I 	IJC6 1 1C 1 17KHHHH &*d+,---r   c           
         t          | t                    r|                                 n| }t          |          dk    r't	          t          |                    \  }}|dk    r|S t          t                    }|D ]G\  }}|                    dd          ^}}|sdn|d         }||         	                    ||f           H	 t          d |                                D                       }g }|D ]i\  }	}
|                    d t          |	t          |          z
            D                        |	                    t          ||
                              j|S # t          $ r! d |                                D             cY S w xY w)	a  Recreate a Python tree from its flat representation.

    .. code-block:: python

        from mlx.utils import tree_unflatten

        d = tree_unflatten([("hello.world", 42)])
        print(d)
        # {"hello": {"world": 42}}

        d = tree_unflatten({"hello.world": 42})
        print(d)
        # {"hello": {"world": 42}}

    Args:
        tree (list[tuple[str, Any]] or dict[str, Any]): The flat representation of a Python tree.
           For instance as returned by :meth:`tree_flatten`.

    Returns:
        A Python tree.
    r?   r4   r3   )maxsplitr   c              3   8   K   | ]}t          |          |fV  d S r   )int)r   idxs     r   r   z!tree_unflatten.<locals>.<genexpr>   s,      AA#s3xxoAAAAAAr   c                     g | ]}i S r   r   )r   _s     r   
<listcomp>z"tree_unflatten.<locals>.<listcomp>   s    444Qb444r   c                 4    i | ]\  }}|t          |          S r   )tree_unflatten)r   r$   vs      r   r%   z"tree_unflatten.<locals>.<dictcomp>   s&    BBBA>!$$BBBr   )r&   r,   r-   lennextiterr   r'   splitappendsortedkeysr@   rangerQ   rB   )r   r-   rF   rG   childrencurrent_idxnext_idxrY   lr   r$   s              r   rQ   rQ      s   , 'tT22<DJJLLLE 5zzQ$u++&&
U"99L 4  H 8 8
U!$3!;!;h%6228A;$$h%67777	CAAAAAAA 	2 	2DAqHH44%CFF
"3"3444555HH^HQK001111 C C CBB1A1ABBBBBBCs   BE   (F
Fc                 J   | ||          r||n | ||          S |}t          |t          t          f          r|D ]}t          | |||          }nOt          |t                    r*|                                D ]}t          | |||          }n||n | ||          S |S )a  Applies a reduction to the leaves of a Python tree.

    This function reduces Python trees into an accumulated result by applying
    the provided function ``fn`` to the leaves of the tree.

    Example:
        >>> from mlx.utils import tree_reduce
        >>> tree = {"a": [1, 2, 3], "b": [4, 5]}
        >>> tree_reduce(lambda acc, x: acc + x, tree, 0)
        15

    Args:
        fn (callable): The reducer function that takes two arguments (accumulator,
            current value) and returns the updated accumulator.
        tree (Any): The Python tree to reduce. It can be any nested combination of
            lists, tuples, or dictionaries.
        initializer (Any, optional): The initial value to start the reduction. If
            not provided, the first leaf value is used.
        is_leaf (callable, optional): A function to determine if an object is a
            leaf, returning ``True`` for leaf nodes and ``False`` otherwise.

    Returns:
        Any: The accumulated value.
    )r&   r'   r(   tree_reducer,   values)r   r   initializerr   accumulatorrE   s         r   r`   r`      s    2 wwt}}"*tt;0E0EEK$u&& F 	F 	FD%b$WEEKK	F	D$		 FKKMM 	F 	FD%b$WEEKK	F #*tt;0E0EEr   c                     t           t          t          t          f          rt	                     dk    rd t          t          t          t          f          rt	                    dk    rd S   S t           t          t          f          rPt          t          t          f          r4t                     } |fdt                     D                       S t           t                    rft          t                    rQ fdt                                                     t                                                    z  D             S t          d                      S )a  Merge two Python trees in one containing the values of both. It can be
    thought of as a deep dict.update method.

    Args:
        tree_a (Any): The first Python tree.
        tree_b (Any): The second Python tree.
        merge_fn (callable, optional): A function to merge leaves.

    Returns:
        The Python tree containing the values of both ``tree_a`` and
        ``tree_b``.
    r   Nc              3   @   K   | ]\  }}t          ||          V  d S r   )
tree_merge)r   abmerge_fns      r   r   ztree_merge.<locals>.<genexpr>5  sD       
 
+/1aJq!X&&
 
 
 
 
 
r   c           
          i | ]<}|t                              |d                               |d                     =S r   )rf   get)r   r$   ri   tree_atree_bs     r   r%   ztree_merge.<locals>.<dictcomp>9  sS     
 
 
 z&**Q--vzz!T/B/BHMM
 
 
r   zOTrees contain elements at the same locations but no merge function was provided)
r&   r,   r'   r(   rS   r)   r   setrY   rB   )rl   rm   ri   r.   s   ``` r   rf   rf     s    &4u-.. 3v;;!3C3C&4u-.. 3v;;!3C3C~&,fn&4-(( (Zu-N-N (<<x 
 
 
 
3>vv3N3N
 
 
 
 
 	
 
FD	!	! (j&>&> (
 
 
 
 
 
''#fkkmm*<*<<
 
 
 	

 ,   x'''r   )r4   NN)NNr   )collectionsr   	itertoolsr   typingr   r   r   r   r	   r
   r   r   r9   strrC   rQ   r`   rf   r   r   r   <module>rs      s:   # # # # # # ! ! ! ! ! ! D D D D D D D D D D D D D D D D D D HL3 3 333$'32:82D33 3 3 3t #'5% 5% 5%5%
5% 5% h	5%
 3-5% 	5% 5% 5% 5%t "&JN	I I
II hI %U38_ 5tCH~ EFG	I
 4c3h $sCx.01I I I IX/CtE#s(O4d38nDE /C# /C /C /C /Cd' ' ' 'T(( (( (( (( (( ((r   