B
    zd[                 @   sn   d Z ddlZddlZddlZddlZddlmZ ddlmZ ddl	m
Z
mZ G dd deZG d	d
 d
ZdS )z&Markov chains with parallel tempering.    N   )	BaseChain)Chain)	ChainDatadetect_dtypesc               @   s  e Zd ZdZdIddZedd	 ZejdJd
d	Zedd Zedd Z	e	jdd Z	edd Z
dd Zedd Zedd Zedd Zedd Zejdd Zedd Zejdd Zed d! Zed"d# Zd$d% ZdKd&d'Zed(d) Zejd*d) Zed+d, Zed-d. Zed/d0 Zed1d2 Zed3d4 Zed5d6 Zed7d8 Zed9d: Zed;d< Zed=d> Zed?d@ Z dAdB Z!dCdD Z"dEdF Z#dGdH Z$dS )LParallelTemperedChaina  A collection of parallel tempered Markov chains.

    Parameters
    ----------
    parameters : list or tuple
        List of the parameter names to sample.
    model : object
        Any object that can be called with keyword arguments that map parameter
        names to values. When called, the object must a tuple of ``logl, logp``
        where ``logp`` is the log prior and ``logl`` is the log likelihood at
        the given point. The model may optionally return a dictionary in
        addition that maps strings to any arbitrary data.
    proposals : list of epsie.proposals instances
        List of proposal classes to use for the parameters. There must be one
        and only one proposal for every parameter. A single proposal may cover
        multiple parameters. Proposals must be instances of classes that
        inherit from :py:class:`epsie.proposals.BaseProposal`.
    betas : array of floats, optional
        Array of inverse temperatures. Each beta must be in range 0 (= infinite
        temperature; i.e., only sample the prior) <= beta <= 1 (= coldest
        temperate; i.e., sample the standard posterior). Default is a single
        beta = 1.
    swap_interval : int, optional
        For a parallel tempered chain, how often to calculate temperature
        swaps. Default is 1 (= swap on every iteration).
    adaptive_annealer : object, optional
        Adaptive annealing that adjusts the temperature levels during runtime.
        By default `None`, meaning no annealing.
    bit_generator : :py:class:`epsie.BIT_GENERATOR` instance, optional
        Use the given random bit generator for generating random variates. If
        an int or None is provided, a generator will be created instead using
        ``bit_generator`` as a seed.
    chain_id : int, optional
        An interger identifying which chain this is. Default is 0.

    Attributes
    ----------
    iteration
    lastclear
    scratchlen
    positions
    stats
    acceptance
    blobs
    start_position
    stats0
    blob0
    current_position
    current_stats
    current_blob
    bit_generator
    random_state
    state
    hasblobs
    chain_id : int or None
        Integer identifying the chain.
          ?r   Nr   c	       	         s   _ _d _|_|_d _d _|_|d k	rD|j j	dkrt
dgdtij	d d_t
dgdtij	d_ _d _d _|_ fddjD _tdd jD _d S )	Nr   acceptance_ratio)dtypesntemps
swap_indexc          
      s,   g | ]$}t d d D j |dqS )c             S   s   g | ]}t |qS  )copydeepcopy).0pr   r   `/work/yifan.wang/ringdown/master-ringdown-env/lib/python3.7/site-packages/epsie/chain/ptchain.py
<listcomp>z   s    z=ParallelTemperedChain.__init__.<locals>.<listcomp>.<listcomp>)bit_generatorchain_idbeta)r   r   )r   r   )r   model
parameters	proposalsselfr   r   r   y   s   z2ParallelTemperedChain.__init__.<locals>.<listcomp>c             s   s   | ]}|j V  qd S )N)transdimensional)r   chainr   r   r   	<genexpr>~   s   z1ParallelTemperedChain.__init__.<locals>.<genexpr>)r   r   _betasbetasswap_interval_temperature_acceptance_temperature_swapsadaptive_annealersetup_annealingr   r   floatintr   _bit_generatorZ_random_generatorr   chainsanyr   )	r   r   r   r   r   r    r#   r   r   r   )r   r   r   r   r   r   __init__Y   s2    


zParallelTemperedChain.__init__c             C   s   | j S )N)r'   )r   r   r   r   r      s    z#ParallelTemperedChain.bit_generatorc             C   s"   |dkrt jd| jd}|| _dS )ae  Sets the random bit generator

        Parameters
        ----------
        bit_generator : :py:class:`epsie.BIT_GENERATOR` instance, optional
            Use the given random bit generator for generating random variates.
            If an int or None is provided, a generator will be created instead
            using ``bit_generator`` as a seed.
        N)stream)epsieZcreate_bit_generatorr   r'   )r   r   r   r   r   r      s    
c             C   s   | j d jS )z$Returns the random number generator.r   )r(   random_generator)r   r   r   r   r-      s    z&ParallelTemperedChain.random_generatorc             C   s   | j jS )z.The current state of the random bit generator.)r   state)r   r   r   r   random_state   s    z"ParallelTemperedChain.random_statec             C   s   || j _dS )zSets the state of bit_generator.

        Parameters
        ----------
        state : dict
            Dictionary giving the state to set.
        N)r   r.   )r   r.   r   r   r   r/      s    	c             C   s   dd t | jD S )aj  Returns the current state of the chain.

        The state consists of everything needed such that setting a chain's
        state using another's state will result in identical results.

        Returns
        -------
        dict :
            Dictionary of ``tk -> chains[tk].state``, where ``tk`` is the
            index of each temperature chain.
        c             S   s   i | ]\}}|j |qS r   )r.   )r   tkr   r   r   r   
<dictcomp>   s    z/ParallelTemperedChain.state.<locals>.<dictcomp>)	enumerater(   )r   r   r   r   r.      s    zParallelTemperedChain.statec             C   s&   x |D ]}| j | ||  qW dS )a  Sets the state of the chain using the given dict.

        .. warning::
           Running this will clear the chain's current memory, and replace its
           current position with what is saved in the state.

        Parameters
        ----------
        state : dict
            Dictionary of ``tk -> dict`` mapping indices of the temperature
            chains to the state they should be set to.
        N)r(   	set_state)r   r.   r0   r   r   r   r3      s    
zParallelTemperedChain.set_statec             C   s   | j d jS )z Whether the model returns blobs.r   )r(   hasblobs)r   r   r   r   r4      s    zParallelTemperedChain.hasblobsc             C   s   | j d jS )z/The number of times the chain has been stepped.r   )r(   	iteration)r   r   r   r   r5      s    zParallelTemperedChain.iterationc             C   s   | j d jS )zMReturns the iteration of the last time the chain memory was cleared.
        r   )r(   	lastclear)r   r   r   r   r6      s    zParallelTemperedChain.lastclearc             C   s   | j d jS )z%The length of the scratch space used.r   )r(   
scratchlen)r   r   r   r   r7      s    z ParallelTemperedChain.scratchlenc             C   sb   x| j D ]
}||_qW | jdkr^y(| j|| j  | j|| j  W n tk
r\   Y nX dS )a  Set the scratch length to the given value.

        This will immediately increase the scratch length to ``n``. If the
        chain is already longer than ``n``, this will have no immediate impact.
        However, the next time ``clear`` is called, the scratch length will
        be reset to ``n``.

        Parameters
        ----------
        n : int
            The length to set.
        r   N)r(   r7   r   r"   Zset_lenr    r!   
ValueError)r   nr   r   r   r   r7      s    

c             C   s   | j S )z8Returns the betas (=1 / temperatures) used by the chain.)r   )r   r   r   r   r      s    zParallelTemperedChain.betasc             C   sv   t |ttfr|g}t |tjs*t|}|dk s@td d|k|dk@ 	 s\t
dt|ddd | _dS )z>Checks that the betas are in the allowed range before setting.g      ?zuNo betas = 1 found. This means that the normal posterior (i.e., likelihood * prior) will not be sampled by the chain.r   r   z!all betas must be in range [0, 1]N)
isinstancer%   r&   numpyZndarrayarrayr)   loggingwarningallr8   sortr   )r   r   r   r   r   r      s    

c             C   s
   d| j  S )z9Returns the temperatures (= 1 / betas) used by the chain.g      ?)r   )r   r   r   r   temperatures  s    z"ParallelTemperedChain.temperaturesc             C   s
   t | jS )z5Returns the number of temperatures used by the chain.)lenr   )r   r   r   r   r     s    zParallelTemperedChain.ntempsc             C   sb   t | jd |}tt| t|d}|| j x$t| jD ]\}}t ||||< q@W |	 S )zConcatenates dictionary attributes over all of the temperatures.

        Parameters
        ----------
        attr : str
            The name of the attribute to get from the chains. The attribute
            is assumed to return a dictionary.
        r   )r
   )
getattrr(   r   listkeysr   extendr   r2   asdict)r   attrdoutr0   r   r   r   r   _concatenate_dicts  s    
z(ParallelTemperedChain._concatenate_dictsc                sF   dkr"t t fdd| j}nt t fdd| j}t|S )zConcatenates array attributes over all of the temperatures.

        Returned array has shape ``[ntemps x] niterations``.
        Nc                s
   t |  S )N)rD   )x)rI   r   r   <lambda>0      z;ParallelTemperedChain._concatenate_arrays.<locals>.<lambda>c                s   t |   S )N)rD   )rM   )rI   itemr   r   rN   2  rO   )rE   mapr(   r<   stack)r   rI   rP   Zarrsr   )rI   rP   r   _concatenate_arrays*  s    z)ParallelTemperedChain._concatenate_arraysc             C   s
   |  dS )zDictionary mapping parameters to their start position.

        If the start position hasn't been set, raises a ``ValueError``.
        start_position)rL   )r   r   r   r   rT   5  s    z$ParallelTemperedChain.start_positionc                s@      | _x0t| jD ]"\} fdd D }||_qW dS )am  Sets the starting position.

        This also evaulates the log likelihood and log prior at the starting
        position, as well as determine if the model returns blob data.

        Parameters
        ----------
        position : dict
            Dictionary mapping parameters to values. Values
            should be numpy arrays with length = ntemps.
        c                s   i | ]} |  |qS r   r   )r   param)positionr0   r   r   r1   L  s    z8ParallelTemperedChain.start_position.<locals>.<dictcomp>N)r   _startr2   r(   rT   )r   rV   r   Zposkr   )rV   r0   r   rT   =  s    
c             C   s
   |  dS )a  Dictionary of the log likelihood and log prior at the start
        position.

        The values of the returned dictionary are arrays of length ``ntemps``,
        ordered by increasing temperature.

        Raises a ``ValueError`` if the start position has not been set yet.
        stats0)rL   )r   r   r   r   rX   O  s    
zParallelTemperedChain.stats0c             C   s   | j r| d}nd}|S )aE  The blob data of the starting position, as a dictionary.

        If ``hasblobs`` is False, just returns None. Otherwise,  the values of
        the returned dictionary are arrays of length ``ntemps``, ordered by
        increasing temperature.

        Raises a ``ValueError`` if ``set_start`` has not been run yet.
        blob0N)r4   rL   )r   blobr   r   r   rY   [  s    
zParallelTemperedChain.blob0c             C   s
   |  dS )zThe history of all of the positions, as a structred array.

        If ``ntemps > 1``, the returned array has shape
        ``ntemps x niterations``. Otherwise, the returned array has shape
        ``niterations``.
        	positions)rS   )r   r   r   r   r[   k  s    zParallelTemperedChain.positionsc             C   s
   |  dS )zThe history of all of the stats, as a structred array.

        If ``ntemps > 1``, the returned array has shape
        ``ntemps x niterations``. Otherwise, the returned array has shape
        ``niterations``.
        stats)rS   )r   r   r   r   r\   u  s    zParallelTemperedChain.statsc             C   s
   |  dS )a  The history of all of acceptance ratios and accepted booleans,
        as a structred array.

        If ``ntemps > 1``, the returned array has shape
        ``ntemps x niterations``. Otherwise, the returned array has shape
        ``niterations``.
        
acceptance)rS   )r   r   r   r   r]     s    	z ParallelTemperedChain.acceptancec             C   s0   | j dkrdS | j dt| | j  }|d jS )aA  The history of the acceptance ratios between temperatures.

        The returned array has shape
        ``ntemps-1 x (niterations/swap_interval)`` if ``ntemps > 1``.
        Otherwise, returns None.

        .. note::
           This does not return a structured array, since there is only
           one field.
        Nr	   )r!   rC   r    T)r   rK   r   r   r   temperature_acceptance  s    
z,ParallelTemperedChain.temperature_acceptancec             C   s0   | j dkrdS | j dt| | j  }|d jS )a)  The history of all of the temperature swaps.

        The returned array has shape ``ntemps x (niterations/swap_interval)``
        if ``ntemps > 1``. Otherwise, returns None.

        .. note::
           This does not return a structured array, since there is only
           one field.
        Nr   )r"   rC   r    r^   )r   rK   r   r   r   temperature_swaps  s    
z'ParallelTemperedChain.temperature_swapsc             C   s   | j r| d}nd}|S )a$  The history of all of the blob data, as a structured array.

        If the model does not return blobs, this is just ``None``.

        If ``ntemps > 1``, the returned array has shape
        ``ntemps x niterations``. Otherwise, the returned array has shape
        ``niterations``.
        blobsN)r4   rS   )r   ra   r   r   r   ra     s    
zParallelTemperedChain.blobsc             C   s
   |  dS )z0Dictionary of the current position of the chain.current_position)rL   )r   r   r   r   rb     s    z&ParallelTemperedChain.current_positionc             C   s
   |  dS )z\Dictionary giving the log likelihood and log prior of the current
        position.
        current_stats)rL   )r   r   r   r   rc     s    z#ParallelTemperedChain.current_statsc             C   s   | j sd}n
| d}|S )zDictionary of the blob data of the current position.

        If the model does not return blobs, just returns ``None``.
        Ncurrent_blob)r4   rL   )r   rZ   r   r   r   rd     s    
z"ParallelTemperedChain.current_blobc             C   sJ   x| j D ]}|  qW | jdkrF| j| j }| j| | j| dS )zClears memory of the current chain, and sets start position to the
        current position.

        New scratch space will be created with length equal to ``scratch_len``.
        r   N)r(   clearr   r7   r    r!   r"   )r   r   tlenr   r   r   re     s    
zParallelTemperedChain.clearc             C   sh   | j | | j| | j| d}| jdkrP| j|| j  |d< | j|| j  |d< | jrd| j| |d< |S )z5Returns all of the chain data at the requested index.)r[   r\   r]   r   r`   r_   ra   )	r[   r\   r]   r   r`   r    r_   Z	_hasblobsra   )r   indexrK   r   r   r   __getitem__  s    
z!ParallelTemperedChain.__getitem__c             C   s>   x| j D ]}|  qW | jdkr:| j| j dkr:|   dS )z:Evolves all of the temperatures by one iteration.
        r   r   N)r(   stepr   r5   r    swap_temperatures)r   r   r   r   r   ri     s    zParallelTemperedChain.stepc                s   j }tj jtd}|d d }t jd }t j}xt jd ddD ]}|| }|d }|d | }	|| }
|| |	|  }|dkrd}d}n(t	|| |	|  } j
 }||k}|r|
||< |||< n|	}|||< qRW  fdd	|D } fd
d	|D } jr( fdd	|D } jrB fdd	|D } j j d }x\t jD ]N\}}|| |j|< || |j|<  jr|| |_ jr^|| |j|< q^W d|i j| j < d|i j| j <  jdk	r   dS )zComputes acceptance ratio between temperatures and swaps them.

        The positions, stats, and (if they exist) blobs are swapped. The
        acceptance is not swapped, however.
        )ZdtypeZloglr:   r   r   g      ?Tc                s   g | ]} j | jqS r   )r(   rb   )r   swk)r   r   r   r   )  s   z;ParallelTemperedChain.swap_temperatures.<locals>.<listcomp>c                s   g | ]} j | jqS r   )r(   rc   )r   rk   )r   r   r   r   +  s   c                s   g | ]} j | jqS r   )r(   _active_props)r   rk   )r   r   r   r   .  s   c                s   g | ]} j | jqS r   )r(   rd   )r   rk   )r   r   r   r   1  s   r	   r   N)rc   r<   Zaranger   r&   Zzerosdiffr   rangeexpr-   uniformr   r4   r5   r6   r2   r(   Z
_positionsZ_statsrl   Z_blobsr!   r    r"   r#   )r   r\   r   ZloglkarsZdbetasr0   rk   ZtjZlogljZswjZlogararZswapuZnew_positionsZ	new_stats
new_activeZ	new_blobsiir   r   )r   r   rj     sX    






z'ParallelTemperedChain.swap_temperatures)r   r   NNr   )N)N)%__name__
__module____qualname____doc__r*   propertyr   setterr-   r/   r.   r3   r4   r5   r6   r7   r   rB   r   rL   rS   rT   rX   rY   r[   r\   r]   r_   r`   ra   rb   rc   rd   re   rh   ri   rj   r   r   r   r   r      sN   9 
'


	r   c               @   sF   e Zd ZdZdZdZdZdddZdd	 Zd
d Z	dd Z
dd ZdS )DynamicalAnnealeru  Class for dynamical parallel tempering.

    This is based on the algorithm described in [1].

    Parameters
    ----------
    tau : int, optional
        Defines the swap iteration at which adjustments have been reduced to
        half their initial amplitude ([1]). Default value is 1000.
    nu : int, optional
        Defines the initial amplitude of adjustments ([1]).
        Default values is 10
    Tmax_prior: bool, optional
        Whether to set the hottest chain temperature to infinity. This only
        rewrites the hottest chain temperature to be infty and keeps the other
        chains as they were. By default sets it to infinity.


    References
    ----------
    [1] W. D. Vousden, W. M. Farr, I. Mandel, Dynamic temperature selection
    for parallel tempering in Markov chain Monte Carlo simulations,
    Monthly Notices of the Royal Astronomical Society, Volume 455,
    Issue 2, 11 January 2016, Pages 1919–1937,
    https://doi.org/10.1093/mnras/stv2422
    N  
   Tc             C   s   |  || || _d S )N)setup_decay_Tmax_prior)r   taunuZ
Tmax_priorr   r   r   r*   d  s    zDynamicalAnnealer.__init__c             C   s    ||kst dS || _|| _dS )z(Set up constants for the vanishing decayz'`tau` must be at least larger than `nu`N)r8   _tau_nu)r   r   r   r   r   r   r   h  s    zDynamicalAnnealer.setup_decayc             C   s0   t t d|dd  | _| jr,d|d< dS )z;Calculates the initial log diffs between temperature levelsg      ?Nr:   g        )r<   logrm   _Sr   )r   r   r   r   r   r$   o  s    z!DynamicalAnnealer.setup_annealingc             C   s   d| j  d d|| j   S )z] Vanishign decay to ensure detailed balance at later stages. Is set
        by `tau` and `nu`g      ?r   )r   r   )r   r5   r   r   r   _decayu  s    zDynamicalAnnealer._decayc                s   |j |j |jd d df  d  dk<  jt fddt|jd D 7  _xHtd|jd D ]4}dd|j|d   t	j|d    |j|< qlW d S )Nr:   g      ?r   c                s*   g | ]"}  |  |d     qS )r   )r   )r   i)rq   r5   r   r   r   r   ~  s   z.DynamicalAnnealer.__call__.<locals>.<listcomp>   )
r5   r    r_   r   r<   r=   rn   r   r   ro   )r   r   r   r   )rq   r5   r   r   __call__z  s    zDynamicalAnnealer.__call__)r}   r~   T)rv   rw   rx   ry   r   r   r   r*   r   r$   r   r   r   r   r   r   r|   E  s   
r|   )ry   r>   r<   r   r,   baser   r   r   Z	chaindatar   r   r   r|   r   r   r   r   <module>   s       +