
     i&                     d    d Z ddlmZ ddlZddlmZmZ  G d de          Z G d d          Z	dS )	am  Analysis results and their aggregation --- :mod:`MDAnalysis.analysis.results`
================================================================================

Module introduces two classes, :class:`Results` and :class:`ResultsGroup`,
used for storing and aggregating data in
:meth:`MDAnalysis.analysis.base.AnalysisBase.run()`, respectively.


Classes
-------

The :class:`Results` class is an extension of a built-in dictionary
type, that holds all assigned attributes in :attr:`self.data` and 
allows for access either via dict-like syntax, or via class-like syntax:

.. code-block:: python

    from MDAnalysis.analysis.results import Results
    r = Results()
    r.array = [1, 2, 3, 4]
    assert r['array'] == r.array == [1, 2, 3, 4]


The :class:`ResultsGroup` can merge multiple :class:`Results` objects.
It is mainly used by :class:`MDAnalysis.analysis.base.AnalysisBase` class, 
that uses :meth:`ResultsGroup.merge()` method to aggregate results from
multiple workers, initialized during a parallel run:

.. code-block:: python

    from MDAnalysis.analysis.results import Results, ResultsGroup
    import numpy as np
    
    r1, r2 = Results(), Results()
    r1.masses = [1, 2, 3, 4, 5]
    r2.masses = [0, 0, 0, 0]
    r1.vectors = np.arange(10).reshape(5, 2)
    r2.vectors = np.arange(8).reshape(4, 2)

    group = ResultsGroup(
        lookup = {
            'masses': ResultsGroup.flatten_sequence,
            'vectors': ResultsGroup.ndarray_vstack
            }
        )

    r = group.merge([r1, r2])
    assert r.masses == list((*r1.masses, *r2.masses))
    assert (r.vectors == np.vstack([r1.vectors, r2.vectors])).all()
    )UserDictN)CallableSequencec                   P     e Zd ZdZd Zd Z fdZ fdZd Zd Z	d Z
d	 Z xZS )
Resultsa  Container object for storing results.

    :class:`Results` are dictionaries that provide two ways by which values
    can be accessed: by dictionary key ``results["value_key"]`` or by object
    attribute, ``results.value_key``. :class:`Results` stores all results
    obtained from an analysis after calling :meth:`~AnalysisBase.run()`.

    The implementation is similar to the :class:`sklearn.utils.Bunch`
    class in `scikit-learn`_.

    .. _`scikit-learn`: https://scikit-learn.org/
    .. _`sklearn.utils.Bunch`: https://scikit-learn.org/stable/modules/generated/sklearn.utils.Bunch.html

    Raises
    ------
    AttributeError
        If an assigned attribute has the same name as a default attribute.

    ValueError
        If a key is not of type ``str`` and therefore is not able to be
        accessed by attribute.

    Examples
    --------
    >>> from MDAnalysis.analysis.base import Results
    >>> results = Results(a=1, b=2)
    >>> results['b']
    2
    >>> results.b
    2
    >>> results.a = 3
    >>> results['a']
    3
    >>> results.c = [1, 2, 3, 4]
    >>> results['c']
    [1, 2, 3, 4]


    .. versionadded:: 2.0.0

    .. versionchanged:: 2.8.0
        Moved :class:`Results` to :mod:`MDAnalysis.analysis.results`
    c                     |t          |           v rt          d| d          t          |t                    r'|                                st          d| d          d S d S )N'z%' is a protected dictionary attributez' is not a valid attribute)dirAttributeError
isinstancestrisidentifier
ValueError)selfkeys     e/srv/www/vhosts/g4struct/public_html/venv/lib/python3.11/site-packages/MDAnalysis/analysis/results.py_validate_keyzResults._validate_keyf   s    #d)) >C>>>   S!! 	B#*:*:*<*< 	B@@@@AAA	B 	B 	B 	B    c                     t          |i |}d|                                v rt          d          i | j        d<   |                     |           d S )Ndataz*'data' is a protected dictionary attribute)dictkeysr   __dict__update)r   argskwargss      r   __init__zResults.__init__n   sZ    t&v&&V[[]]"" !NOOO "fFr   c                 v    |                      |           t                                          ||           d S N)r   super__setitem__)r   r   item	__class__s      r   r!   zResults.__setitem__u   s7    3C&&&&&r   c                     |dk    r$t                                          ||           d S |                     ||           d S )Nr   )r    __setattr__r!   )r   attrvalr#   s      r   r%   zResults.__setattr__y   sH    6>>GGc*****T3'''''r   c                 `    	 | |         S # t           $ r}t          d| d          |d }~ww xY wNz#'Results' object has no attribute 'r	   KeyErrorr   r   r&   errs      r   __getattr__zResults.__getattr__   sS    	: 	 	 	 =d=== 	s   
 
-(-c                 Z    	 | |= d S # t           $ r}t          d| d          |d }~ww xY wr)   r*   r,   s      r   __delattr__zResults.__delattr__   sT    	T


 	 	 	 =d=== 	s    
*%*c                     | j         S r   r   )r   s    r   __getstate__zResults.__getstate__   s
    yr   c                     || _         d S r   r2   )r   states     r   __setstate__zResults.__setstate__   s    			r   )__name__
__module____qualname____doc__r   r   r!   r%   r.   r0   r3   r6   __classcell__)r#   s   @r   r   r   9   s        * *XB B B  ' ' ' ' '( ( ( ( (            r   r   c                   l   e Zd ZdZddeeef         fdZ	 ddee	         de
de	fd	Zed
ee         fd            Zed
eej                 fd            Zed
eej                 fd            Zedee         fd            Zed
eej                 fd            Zed
eej                 fd            ZdS )ResultsGroupa  
    Management and aggregation of results stored in :class:`Results` instances.

    A :class:`ResultsGroup` is an optional description for :class:`Result` "dictionaries"
    that are used in analysis classes based on :class:`AnalysisBase`. For each *key* in a
    :class:`Result` it describes how multiple pieces of the data held under the key are
    to be aggregated. This approach is necessary when parts of a trajectory are analyzed
    independently (e.g., in parallel) and then need to me merged (with :meth:`merge`) to
    obtain a complete data set.

    Parameters
    ----------
    lookup : dict[str, Callable], optional
        aggregation functions lookup dict, by default None

    Examples
    --------

    .. code-block:: python

        from MDAnalysis.analysis.results import ResultsGroup, Results
        group = ResultsGroup(lookup={'mass': ResultsGroup.float_mean})
        obj1 = Results(mass=1)
        obj2 = Results(mass=3)
        assert {'mass': 2.0} == group.merge([obj1, obj2])


    .. code-block:: python

        # you can also set `None` for those attributes that you want to skip
        lookup = {'mass': ResultsGroup.float_mean, 'trajectory': None}
        group = ResultsGroup(lookup)
        objects = [Results(mass=1, skip=None), Results(mass=3, skip=object)]
        assert group.merge(objects, require_all_aggregators=False) == {'mass': 2.0}

    .. versionadded:: 2.8.0
    Nlookupc                     || _         d S r   )_lookup)r   r>   s     r   r   zResultsGroup.__init__   s    r   Tobjectsrequire_all_aggregatorsreturnc                 4   t          |          dk    r
|d         }|S t                      }|d                                         D ]P| j                            d          }|fd|D             } ||          |<   <|rt          d          Q|S )a*  Merge multiple Results into a single Results instance.

        Merge multiple :class:`Results` instances into a single one, using the
        `lookup` dictionary to determine the appropriate aggregator functions for
        each named results attribute. If the resulting object only contains a single
        element, it just returns it without using any aggregators.

        Parameters
        ----------
        objects : Sequence[Results]
            Multiple :class:`Results` instances with the same data attributes.
        require_all_aggregators : bool, optional
            if True, raise an exception when no aggregation function for a
            particular argument is found. Allows to skip aggregation for the
            parameters that aren't needed in the final object --
            see :class:`ResultsGroup`.

        Returns
        -------
        Results
            merged :class:`Results`

        Raises
        ------
        ValueError
            if no aggregation function for a key is found and ``require_all_aggregators=True``
           r   Nc                      g | ]
}|         S  rG   ).0objr   s     r   
<listcomp>z&ResultsGroup.merge.<locals>.<listcomp>   s    <<<SC<<<r   z No aggregation function for key=)lenr   r   r@   getr   )r   rA   rB   merged_resultsagg_functionresults_of_tr   s         @r   mergezResultsGroup.merge   s    < w<<1$QZN!! 1:??$$ 	H 	HC<++C66L'<<<<G<<<&2l<&@&@s##( H !F!F!FGGGHr   arrsc                     d | D             S )zFlatten a list of lists into a list

        Parameters
        ----------
        arrs : list[list]
            list of lists

        Returns
        -------
        list
            flattened list
        c                     g | ]	}|D ]}|
S rG   rG   )rH   sublistr"   s      r   rJ   z1ResultsGroup.flatten_sequence.<locals>.<listcomp>   s%    ===W==T====r   rG   rQ   s    r   flatten_sequencezResultsGroup.flatten_sequence   s     >=D====r   c                 R    t          j        |                               d          S )a   sums an ndarray along ``axis=0``

        Parameters
        ----------
        arrs : list[np.ndarray]
            list of input arrays. Must have the same shape.

        Returns
        -------
        np.ndarray
            sum of input arrays
        r   axis)nparraysumrU   s    r   ndarray_sumzResultsGroup.ndarray_sum   s#     x~~!!q!)))r   c                 R    t          j        |                               d          S )a  calculates mean of input ndarrays along ``axis=0``

        Parameters
        ----------
        arrs : list[np.ndarray]
            list of input arrays. Must have the same shape.

        Returns
        -------
        np.ndarray
            mean of input arrays
        r   rX   rZ   r[   meanrU   s    r   ndarray_meanzResultsGroup.ndarray_mean  s#     x~~"""***r   floatsc                 N    t          j        |                                           S )zcalculates mean of input float values

        Parameters
        ----------
        floats : list[float]
            list of float values

        Returns
        -------
        float
            mean value
        r_   )rb   s    r   
float_meanzResultsGroup.float_mean  s      x$$&&&r   c                 *    t          j        |           S )zPerforms horizontal stack of input arrays

        Parameters
        ----------
        arrs : list[np.ndarray]
            input numpy arrays

        Returns
        -------
        np.ndarray
            result of stacking
        )rZ   hstackrU   s    r   ndarray_hstackzResultsGroup.ndarray_hstack,       yr   c                 *    t          j        |           S )zPerforms vertical stack of input arrays

        Parameters
        ----------
        arrs : list[np.ndarray]
            input numpy arrays

        Returns
        -------
        np.ndarray
            result of stacking
        )rZ   vstackrU   s    r   ndarray_vstackzResultsGroup.ndarray_vstack<  rh   r   r   )T)r7   r8   r9   r:   r   r   r   r   r   r   boolrP   staticmethodlistrV   rZ   ndarrayr]   ra   floatrd   rg   rk   rG   r   r   r=   r=      s       $ $L tCM2     KO* *(*CG*	* * * *X >tDz > > > \> *$rz* * * * \* +4
+ + + + \+ '4; ' ' ' \' T"*-    \ T"*-    \  r   r=   )
r:   collectionsr   numpyrZ   typingr   r   r   r=   rG   r   r   <module>rt      s   1 1f !               % % % % % % % %Z Z Z Z Zh Z Z Zzt t t t t t t t t tr   