
    GJj,                     X   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y)    )defaultdict)zip_longest)AnyCallableDictListOptionalTupleUnionN)is_leaffntreerestr   returnc          
           |      r	  |g S t        |t        t        f      r:t        |      } fdt	        |      D        }t        |d      r || S  ||      S t        |t              r<|j                         D ci c]  \  }t         |gfdD        di! c}}S   |g S c c}}w )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``.
    c              3   X   K   | ]   \  }t        |gfd D        di " yw)c              3   (   K   | ]	  }|     y wN .0ris     S/Users/ahmed/devFolder/claude-voice/.venv/lib/python3.12/site-packages/mlx/utils.py	<genexpr>z%tree_map.<locals>.<genexpr>.<genexpr>1   s     !51!A$   r   N)tree_map)r   childr   r   r   r   s     @r   r   ztree_map.<locals>.<genexpr>0   s4      
+5 RG!5!5GwG+s   &*_fieldsc              3   (   K   | ]	  }|     y wr   r   r   r   ks     r   r   ztree_map.<locals>.<genexpr>7   s     $84aQqT4r   r   )	
isinstancelisttupletype	enumeratehasattrdictitemsr   )r   r   r   r   TreeTypesubtreesr"   r   s   ` ``  ` r   r   r      s    H wt}$	D4-	(:
%dO
 '.dI&>x"VHXDVV	D$	 !JJL
(5 xEJ$84$8J'JJ(
 	

 $
s   $B=r   pathr.   c                     |      r
  ||g S t        |t        t        f      r1|r| dndt        |      } | fdt	        |      D              S t        |t
              rJ|r| dnd|j                         D ci c]$  \  }t         |gfdD          d& c}}S   ||g S c c}}w )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
    . c              3   b   K   | ]%  \  }t        |gfd D          d ' yw)c              3   (   K   | ]	  }|     y wr   r   r   s     r   r   z/tree_map_with_path.<locals>.<genexpr>.<genexpr>f        04aQqT4r   r-   N)tree_map_with_path)r   r   r   r   r   prefixr   s     @r   r   z%tree_map_with_path.<locals>.<genexpr>d   sL      
 ,5 E040:A6(STRU ,s   +/c              3   (   K   | ]	  }|     y wr   r   r!   s     r   r   z%tree_map_with_path.<locals>.<genexpr>n   r4   r   r-   )r#   r$   r%   r&   r'   r)   r*   r5   )	r   r   r   r.   r   r+   r"   r   r6   s	   ` ` ` ` @r   r5   r5   >   s   B wt}$$t$$	D4-	(#D6: 
 &dO	
 
 	
 
D$	#D6
 !JJL	
 )5 !E040:A6(STRU  )	
 	
 $$t$$
s   )Cr6   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              r-| j                         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   r0   )
r#   r$   extendr)   update
ValueErrorr%   r'   tree_flattenr*   )	r   r6   r   r8   _add_to_destinationr   itemkeyvalues	            r   r>   r>   v   s   P 
 +t$)00	K	&)00VWW wt}fQRj$/01 $u& GAt&1#E ' $**,JC6(!C5 17KH ' &*d+,-    c           	         t        | t              r| j                         n| }t        |      dk(  rt	        t        |            \  }}|dk(  r|S t        t              }|D ]9  \  }}|j                  dd      ^}}|sdn|d   }||   j                  ||f       ; 	 t        d |j                         D              }g }|D ]V  \  }	}
|j                  t        |	t        |      z
        D cg c]  }i  c}       |j                  t        ||
                X |S c c}w # t        $ r7 |j                         D 
ci c]  \  }
}|
t        |       nc c}}
w c}}
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:   r1   r0   )maxsplitr   c              3   6   K   | ]  }t        |      |f  y wr   )int)r   idxs     r   r   z!tree_unflatten.<locals>.<genexpr>   s     A#s3xos   )r#   r)   r*   lennextiterr   r$   splitappendsortedkeysr;   rangetree_unflattenr=   )r   r*   rA   rB   childrencurrent_idxnext_idxrO   lr   r"   _vs                r   rQ   rQ      sT   , 'tT2DJJLE 5zQ$u+&
U"9L 4 H
U!$3!;h%28A;$$h%67 	CAAADAqHH%CF
"34"3Qb"345HH^HQK01   5  C191AB1AA>!$$1ABBCs1   AD! +	D
4'D! D! !E!=EE! E!c                    | ||      r||S  | ||      S |}t        |t        t        f      r|D ]  }t        | |||      } |S t        |t              r%|j                         D ]  }t        | |||      } |S ||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   accumulatorr@   s         r   rY   rY      s    2 wt}"*tE;0EEK$u&D%b$WEK   
D$	KKMD%b$WEK "
  #*tE;0EErC   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      r<t        |t        t        f      r&t        |       } |fdt        | |      D              S t        | t              r}t        |t              rmt        | j                               t        |j                               z  D ci c]0  }|t        | j                  |d      |j                  |d            2 c}S t        d       | |      S c c}w )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        ||        y wr   )
tree_merge)r   abmerge_fns      r   r   ztree_merge.<locals>.<genexpr>6  s#      
3N41aJq!X&3Ns   zOTrees contain elements at the same locations but no merge function was provided)r#   r)   r$   r%   rI   r&   r   setrO   r_   getr=   )tree_atree_brb   r+   r"   s     `  r   r_   r_     sN    &4u-.3v;!3C&4u-.3v;!3C~&,fn&4-(Zu-N< 
3>vv3N
 
 	
 
FD	!j&> '#fkkm*<<
< z&**Q-vzz!T/BHMM<
 	

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