B
    zd&                 @   s2   d Z ddlZddlmZ G dd dZdd ZdS )z"Utilities for handling chain data.    N)
array2dictc               @   s   e Zd ZdZdddZedd Zedd	 Zejd
d	 Zdd Z	dd Z
dd ZdddZdd Zdd ZdddZdd ZdS )	ChainDataa  Handles reading and adding data to a chain.

    When initialized, a list of parameter names must be provided for which the
    data will be stored. Items may then be added by giving a dictionary mapping
    parameter names to their values. See the Examples below. If the index given
    where the new data should be added is larger then the length of the
    instance's current memory, it will automatically be extended by the amount
    needed. Scratch space may be allocated ahead of time by using the
    ``extend`` method.

    Data can be retrieved using the ``.data`` attribute, which will return the
    data as a numpy structred array.

    Space for multiple temperatures may be specified by providing the
    ``ntemps`` argument. In this case, the array will have shape
    ``niterations x ntemps``.

    .. note::

        Note that the temperatures are the last index. This is because numpy is
        row major. When stepping a chain, the collection of temperatures at a
        given iteration are often accessed, to write data, and to do
        temperature swaps. However, once the chain is complete, it is more
        common to access all the iterations at once for a given temperature;
        e.g., to calculate autocorrelation length. For this reason, it is
        recommended that the chain data be transposed before doing other
        operations and writing to disk.

    Parameters
    ----------
    parameters : list or tuple of str
        The names of the parameters to store data for.
    dtypes : dict, optional
        Dictionary mapping parameter names to types. Will default to using
        ``float`` for any parameters that are not provided.
    ntemps : int, optional
        The number of temperatures used by the chain. Default is 1.

    Attributes
    ----------
    parameters : tuple
        The names of the parameters that were given.
    dtypes : dict
        The data type used for each of the parameters.
    data

    Examples
    --------
    Create an scratch space for two parameters, "x" and "y". Note that,
    initially, the data attribute returns None, and the length is zero:

    >>> from epsie.chain import ChainData
    >>> chaindata = ChainData(['x', 'y'])
    >>> print(len(chaindata))
    0
    >>> print(chaindata.data)
    None

    Now add some data by passing a dictionary of values. Note that the length
    is automatically extended to accomodate the given index, with zeroes filled
    in up to that point:

    >>> chaindata[1] = {'x': 2.5, 'y': 1.}
    >>> chaindata.data
    array([(0. , 0.), (2.5, 1.)], dtype=[('x', '<f8'), ('y', '<f8')])
    >>> len(chaindata)
    2

    Manually extend the scratch space, and fill it. Note that we can set
    multiple values at once using standard slicing syntax:

    >>> chaindata.extend(4)
    >>> chaindata[2:] = {'x': [3.5, 4.5, 5.5, 6.5], 'y': [2, 3, 4, 5]}
    >>> chaindata.data
    array([(0. , 0.), (2.5, 1.), (3.5, 2.), (4.5, 3.), (5.5, 4.), (6.5, 5.)],
      dtype=[('x', '<f8'), ('y', '<f8')])

    Since we did not specify dtypes, the data types have all defaulted to
    floats. Change 'y' to be ints instead:

    >>> chaindata.dtypes = {'y': int}
    >>> chaindata.data
    array([(0. , 0), (2.5, 1), (3.5, 2), (4.5, 3), (5.5, 4), (6.5, 5)],
      dtype=[('x', '<f8'), ('y', '<i8')])

    Clear the memory, and set the new length to be 3:

    >>> chaindata.clear(3)
    >>> chaindata.data
    array([(0., 0), (0., 0), (0., 0)], dtype=[('x', '<f8'), ('y', '<i8')])

    N   c             C   s2   t || _d | _i | _|d kr"i }|| _|| _d S )N)tuple
parameters_data_dtypesdtypesntemps)selfr   r	   r
    r   b/work/yifan.wang/ringdown/master-ringdown-env/lib/python3.7/site-packages/epsie/chain/chaindata.py__init__u   s    
zChainData.__init__c          
   C   sD   y| j S  tk
r> } z| j dkr&dS t|W dd}~X Y nX dS )zReturns the saved data as a numpy structered array.

        If no data has been added yet, and an initial length was not specified,
        returns ``None``.
        N)r   AttributeError)r   er   r   r   data~   s    
zChainData.datac             C   s   | j S )z7Dictionary mapping parameter names to their data types.)r   )r   r   r   r   r	      s    zChainData.dtypesc                s    fdd|D }t |r.tdd| j|  j fdd jD  t fdd jD  _	 j
dk	r j
 j	 _
dS )	a.  Sets/updates the dtypes to the given dictionary.

        If data has already been saved for a parameter, an attempt will be
        made to cast the data to the new data type.

        A ``ValueError`` will be raised if a parameter name is in the
        dictionary that was not provided upon initialization.

        Parameters
        ----------
        dtypes : dict
            Dictionary mapping parameter names to data types. The data type
            of any parameters not provided will remain their current/default
            (float) type.
        c                s   g | ]}| j kr|qS r   )r   ).0p)r   r   r   
<listcomp>   s    z$ChainData.dtypes.<locals>.<listcomp>zunrecognized parameter(s) {}z, c                s   i | ]}| j krt|qS r   )r   float)r   r   )r   r   r   
<dictcomp>   s    z$ChainData.dtypes.<locals>.<dictcomp>c                s   g | ]}| j | fqS r   )r	   )r   r   )r   r   r   r      s   N)any
ValueErrorformatjoinr   updater   numpydtype_npdtyper   Zastype)r   r	   Zunrecognizedr   )r   r   r	      s    
c             C   s   | j d krdS | j jd S d S )Nr   )r   shape)r   r   r   r   __len__   s    
zChainData.__len__c             C   sX   | j dkr|}n
|| j f}tj|tj| jd}| jdkr@|| _ntj| j|dd| _dS )z*Extends scratch space by n items.
        r   )r   Nr   )Zaxis)r
   r   fullnanr   r   append)r   nZnewshapenewr   r   r   extend   s    


zChainData.extendc             C   s4   t | }||k r | ||  ntd||dS )zsSets the data length to ``n``.

        If the data length is already > ``n``, a ``ValueError`` is raised.
        z.current length ({}) is already greater than {}N)lenr&   r   r   )r   r$   Zlselfr   r   r   set_len   s
    zChainData.set_lenc             C   s    d| _ |dkrd}| | dS )zClears the memory.

        Parameters
        ----------
        newlen : int, optional
            If provided, will create new scratch space with the given length.
        Nr   )r   r&   )r   Znewlenr   r   r   clear   s    zChainData.clearc             C   s
   t | jS )N)reprr   )r   r   r   r   __repr__   s    zChainData.__repr__c             C   s   | j d krtd| j | S )Nzno data has been set yet)r   r   )r   indexr   r   r   __getitem__   s    
zChainData.__getitem__c             C   s"   |dkrt | jS t | | S dS )zReturns the data as a dictionary.

        Parameters
        ----------
        index : slice, optional
            Only get the elements indicated by the given slice before
            converting to a dictionary.
        N)r   r   )r   r,   r   r   r   asdict   s    	
zChainData.asdictc          	   C   s   y| j | }W n8 ttfk
rF   | |d t|   | j | }Y nX t|trxt|tsxx$|D ]}|| ||< qbW n
|| j |< d S )Nr   )r   
IndexError	TypeErrorr&   r'   
isinstancedictstr)r   r,   valueelemr   r   r   r   __setitem__   s    
zChainData.__setitem__)Nr   )N)N)__name__
__module____qualname____doc__r   propertyr   r	   setterr    r&   r(   r)   r+   r-   r.   r6   r   r   r   r   r      s   \
	!

r   c             C   s$   t | tst| } dd |  D S )a  Convenience function to detect the dtype of some data.

    Parameters
    ----------
    data : dict or numpy.ndarray
        Either a numpy structred array/void or a dictionary mapping parameter
        names to some (arbitrary) values. The values may be either arrays or
        atomic data. If the former, the dtype will be taken from the array's
        dtype.

    Returns
    -------
    dict :
        Dictionary mapping the parameter names to types.
    c             S   s,   i | ]$\}}t |tjr|jnt||qS r   )r1   r   Zndarrayr   type)r   r   valr   r   r   r     s   z!detect_dtypes.<locals>.<dictcomp>)r1   r2   r   items)r   r   r   r   detect_dtypes  s    
r@   )r:   r   Zepsier   r   r@   r   r   r   r   <module>   s
    r