B
    d                @   s  d Z ddlmZ yddlmZ W n ek
r<   edZY nX ddl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ZddlmZmZ ddlZddlmZ dd	lmZ dd
lmZ ddlmZ ddlZddlmZ dZddlm Z! ddlm"Z# dd Z$G dd de%Z&G dd de%Z'G dd de%Z(G dd de%Z)G dd de%Z*G dd de%Z+G dd  d e%Z,G d!d" d"e%Z-G d#d$ d$e%Z.dS )%z+
Generic time interval coincidence engine.
    )bisect_left)NegInfz-infN)spatial)ChainMapCounter)ligolw)	lsctables)coincs)segments   )offsetvectorz"Kipp Cannon <kipp.cannon@ligo.org>)date)versionc             C   s0   t j|  jt j| j }t||  t j S )a  
	Compute and return the time required for light to travel through
	free space the distance separating the two instruments.  The inputs
	are two instrument prefixes (e.g., "H1"), and the result is
	returned in seconds.  Note how this differs from LAL's
	XLALLightTravelTime() function, which takes two detector objects as
	input, and returns the time truncated to integer nanoseconds.
	)lalZcached_detector_by_prefixlocationmathsqrtsumC_SI)Zinstrument1Zinstrument2Zdx r   _/work/yifan.wang/ringdown/master-ringdown-env/lib/python3.7/site-packages/lalburst/snglcoinc.pylight_travel_timeH   s    	r   c               @   sT   e Zd ZdZedd Zdd Zdd Zedd	 Z	ed
d Z
dd Zdd ZdS )singlesqueuea  
	Queue a stream of partially time ordered events:  as new events are
	added, sort them into the queue in time order;  maintain a record
	of the time up-to which the list of events is complete;  and
	provide events sorted in time order to coincidence tests.

	Searches must define a class subclassed from this that defines an
	override of the .event_time() method.  See coincgen_doubles for
	information on what to do with the subclass.

	Implementation notes:

	What is actually inserted into the queue is the time of the event
	as defined by the .event_time() method provided by the subclass.
	This allows sorts and bisection searches to be performed quickly,
	without the need for much additional code.  When we need to
	retrieve an event, we need some way of turning the time of
	the event (i.e., the thing that is actually in the queue) back into
	that event, and this is done by adding an attribute to the time
	object that points back to the original event.  This means that
	builtin Python numeric types cannot be used for the times of the
	events because new attributes cannot be attached to them.
	Subclasses of the builtin numeric types can be used as time
	objects, as can be LAL's LIGOTimeGPS.
	c             C   s   t dS )a7  
		Override with a method to return the "time" of the given
		event.  The "time" object that is returned is typically a
		lal.LIGOTimeGPS object.  If some other type is used, it
		must support arithmetic and comparison with python float
		objects and lal.LIGOTimeGPS objects, and support comparison
		with ligo.segments.PosInfinity and
		ligo.segments.NegInfinity, and it must have a .__dict__ or
		other mechanism allowing an additional attribute named
		.event to be set on the object.  The builtin float type is
		not suitable, but a subclass of float would be.
		N)NotImplementedError)eventr   r   r   
event_timem  s    zsinglesqueue.event_timec             C   s   |  |}||_|S )zd
		For internal use.  Construct the object that is to be
		inserted into the queue from an event.
		)r   r   )selfr   entryr   r   r   queueentry_from_event}  s    
z"singlesqueue.queueentry_from_eventc             C   s8   || _ |dk rtd| || _tj| _g | _i | _dS )a  
		Initialize a new, empty, singles queue.  offset is the time
		that will be added to events in this queue when forming
		coincidences with events from other queues, and
		max_coinc_window is an upper bound on the maximum time that
		can separate events in this queue from events in other
		queues and they still be able to form coincidences --- it
		is not that maximum time, necessarily, but the true maximum
		time cannot be larger than max_coinc_window.

		Note that max_coinc_window is not defining the coincidence
		test, that is defined by the get_coincs machinery within
		the coincgen_doubles class.  max_coinc_window is used by
		the streaming logic to determine where in the event
		sequences complete n-tuple candidates can be constructed.
		Although max_coinc_window need only bound the time interval
		described above, the latency of the coincidence engine is
		reduced and the number of events stored in the queue (and
		the computational overhead they cause) is reduced if the
		number is as small as possible.
		g        zmax_coinc_window < 0 (%g)N)offset
ValueErrormax_coinc_windowr
   NegInfinity
t_completequeueindex)r   r   r!   r   r   r   __init__  s    zsinglesqueue.__init__c             C   s   | j r| j d S | jS )a  
		Using .event_time() to define the times of events, the time
		of the oldest event in the queue or self.t_complete if the
		queue is empty.  The time scale for .age is that of the
		events themselves, not their shifted times (the offset
		parameter is not applied).

		This is not used by the coincidence engine.  This
		information is provided as a convenience for calling code.
		For example, some performance improvements might be
		realized if segment lists or other information are clipped
		to the range of times spanned by the contents of the
		coincidence engine queues, and this allows calling codes to
		see what interval that is, in physical event times not
		time-shifted event times.
		r   )r$   r#   )r   r   r   r   age  s    zsinglesqueue.agec             C   s   | j | j | j S )a  
		The time up to which the time-shifted events in other
		detectors' queues can be considered complete for the
		purpose of forming n-tuple coincidences with the
		time-shifted events in this queue.  This precedes the
		(time-shifted) time up to which this event list is complete
		by the maximum coincidence window that can separate an
		event in this queue from events in others and they still be
		considered coincident in time.

		The earliest such time across all detectors is the time up
		to which the n-tuple candidate list can be completely
		constructed, assuming the "time" of an n-tuple candidate is
		defined to be the earliest time-shifted time of the events
		in the n-tuple.
		)r#   r   r!   )r   r   r   r   t_coinc_complete  s    zsinglesqueue.t_coinc_completec             C   s^   || j k rtd| j |f |rT| jdd |D  | jt| j| | j  || _ dS )a  
		Add new events to the queue.  Mark the queue complete up to
		t_complete, meaning you promise no further events will be
		added to the queue earlier than t_complete.  The time scale
		for t_complete is that of the events themselves, not their
		shifted times (the offset parameter is not applied).

		NOTE:  the events sequence will be iterated over multiple
		times.  It may not be a generator.
		z6t_complete has gone backwards:  last was %s, new is %sc             s   s   | ]}t ||fV  qd S )N)id).0r   r   r   r   	<genexpr>  s    z$singlesqueue.push.<locals>.<genexpr>N)	r#   r    r%   updater$   extendmapr   sort)r   eventsr#   r   r   r   push  s    

zsinglesqueue.pushc          	   C   s   |dkr>t dd | jD }| jdd= | j  tj| _|S || j8 }|| j | jkrxt	d|| j | j| j| jf t
| j|| j }t dd | jd| D }| jd|= x|D ]}| jt| qW |t dd | jdt
| j|| j  D  S )a  
		t is the time up to which the calling code is in the
		process of constructing all available n-tuple coincident
		candidates.  The return value is a tuple of all events from
		the queue whose time-shifted end times are up to (not
		including) t + max_coinc_window, which are all the events
		from this queue that could form a coincidence with an event
		up to (not including) t.

		t is defined with respect to the time-shifted event times.
		The contents of the returned tuple is time-ordered as
		defined by .event_time().

		Assuming that t cannot go backwards, any of the reported
		events that cannot be used again are removed from the
		internal queue.

		If t is None, then the queue is completely flushed.  All
		remaining events are pulled from the queue and reported in
		the tuple, .t_complete is reset to -inf and all other
		internal state is reset.  After calling .pull() with t =
		None, the object's state is equivalent to its initial state
		and it is ready to process a new stream of events.
		Nc             s   s   | ]}|j V  qd S )N)r   )r*   r   r   r   r   r+     s    z$singlesqueue.pull.<locals>.<genexpr>zfpull to %g fails to precede time to which queue is complete, (%g + %g), by the required margin of %g sc             s   s   | ]}|j V  qd S )N)r   )r*   r   r   r   r   r+   )  s    c             s   s   | ]}|j V  qd S )N)r   )r*   r   r   r   r   r+   1  s    )tupler$   r%   clearr
   r"   r#   r   r!   r    r   popr)   )r   tr0   iZflushed_eventsr   r   r   r   pull  s    

 
zsinglesqueue.pullN)__name__
__module____qualname____doc__staticmethodr   r   r&   propertyr'   r(   r1   r7   r   r   r   r   r   S  s   -r   c               @   sh   e Zd ZdZeZG dd deZdd Zedd Z	edd	 Z
ed
d Zdd Zdd Zdd ZdS )coincgen_doublesa  
	Using a pair of singlesqueue objects, constructs pairs of
	coincident events from streams of partially time-ordered events.

	Searches must subclass this.  The .singlesqueue class attribute
	must be set to the search-specific singlesqueue implementation to
	be used, i.e., a subclass of singlesqueue with an appropriate
	.event_time() override.  The internally-defined .get_coincs class
	must be overridden with an implementation that provides the
	required .__init__() and .__call__() methods.
	c               @   s    e Zd ZdZdd Zdd ZdS )zcoincgen_doubles.get_coincsa  
		This class defines the coincidence test.  An instance is
		initialized with a sequence of events, and is a callable
		object.  When the instance is called with a single event
		from some other instrument, a time offset to apply to that
		event (relative to the events in this list) and a time
		coincidence window, the return value must be a (possibly
		empty) sequence of the initial events that are coincident
		with that given event.

		The sequence of events with which the instance is
		initialized is passed in time order.

		It is not required that the implementation be subclassed
		from this.  This placeholder implementation is merely
		provided to document the required interface.

		A minimal example:

		class get_coincs(object):
			def __init__(self, events):
				self.events = events
			def __call__(self, event_a, offset_a, coinc_window):
				return [event_b for event_b in self.events if abs(event_a.time + offset_a - event_b.time) < coinc_window]

		This is performance-critical code and a naive
		implementation such as the one above will likely be found
		to be inadequate.  Expect to implement this code in C for
		best results.
		c             C   s   t dS )a	  
			Prepare to search a collection of events for
			coincidences with other single events.  events is a
			time-ordered iterable of events.  It is recommended
			that any additional indexing required to improve
			search performance be performed in this method.
			N)r   )r   r0   r   r   r   r&   f  s    z$coincgen_doubles.get_coincs.__init__c             C   s   t dS )aV  
			Return a sequence of the events from those passed
			to .__init__() that are coincident with event_a.
			The sequence need not be in time order.  The object
			returned by this method must be iterable and
			support being passed to bool() to test if it is
			empty.

			offset_a is the time shift to be added to the time
			of event_a before comparing to the times of events
			passed to .__init__().  This behaviour is to
			support the construction of time shifted
			coincidences.

			coinc_window is the maximum time, in seconds,
			separating coincident events from the shifted time of
			event_a.  Here, this is the interval that defines
			the coincidence test between the two detectors, not
			the bound on all such intervals used by the
			singlesqueue object to determine the interval for
			which n-tuple candidates can be constructed.
			N)r   )r   Zevent_aoffset_acoinc_windowr   r   r   __call__p  s    z$coincgen_doubles.get_coincs.__call__N)r8   r9   r:   r;   r&   rA   r   r   r   r   
get_coincsE  s   
rB   c                s   t |dkrtdt | |t| _jdk r@tdt t|   dk r`td  t fdd| D _t	dd j D  _
d	S )
a  
		offset_vector must be a two-instrument offset vector.  This
		sets which two instruments' events are to be processed by
		this object, and the offsets that should be applied to
		their events when searching for coincidences.
		coinc_windows is a dictionary mapping pairs of instruments
		(as sets) to the largest time that can separate events from
		those pairs of instruments (after applying the required
		time offsets) and they still be considered coincident.
		   z6offset_vector must name exactly 2 instruments (got %d)g        zcoinc_window < 0 (%g)zmax(coinc_window) < 0 (%g)c             3   s"   | ]\}}| | fV  qd S )N)r   )r*   
instrumentr   )r!   r   r   r   r+     s    z,coincgen_doubles.__init__.<locals>.<genexpr>c             s   s   | ]}|j V  qd S )N)r%   )r*   r$   r   r   r   r+     s    N)lenr    	frozensetr@   maxvaluesdictitemsqueuesr   r%   )r   offset_vectorcoinc_windowsr   )r!   r   r   r&     s    

zcoincgen_doubles.__init__c             C   s   t dd | j D S )Nc             s   s   | ]\}}||j fV  qd S )N)r   )r*   rD   r$   r   r   r   r+     s    z1coincgen_doubles.offset_vector.<locals>.<genexpr>)rI   rK   rJ   )r   r   r   r   rL     s    zcoincgen_doubles.offset_vectorc             C   s   t dd | j D S )z0
		The earliest of the internal queues' .age.
		c             s   s   | ]}|j V  qd S )N)r'   )r*   r$   r   r   r   r+     s    z'coincgen_doubles.age.<locals>.<genexpr>)minrK   rH   )r   r   r   r   r'     s    zcoincgen_doubles.agec             C   s   t dd | j D S )z=
		The earliest of the internal queues' .t_coinc_complete.
		c             s   s   | ]}|j V  qd S )N)r(   )r*   r$   r   r   r   r+     s    z4coincgen_doubles.t_coinc_complete.<locals>.<genexpr>)rN   rK   rH   )r   r   r   r   r(     s    z!coincgen_doubles.t_coinc_completec             C   s   | j | || dS )ax  
		Push new events from some instrument into the internal
		queues.  The iterable of events need not be time-ordered.
		With self.singlesqueue.event_time() defining the time of
		events, t_complete sets the time up-to which the collection
		of events is known to be complete.  That is, in the future
		no new events will be pushed whose times are earlier than
		t_complete.
		N)rK   r1   )r   rD   r0   r#   r   r   r   r1     s    
zcoincgen_doubles.pushc             c   s   t |t |kr*|| }}| }dd }ndd }|  |dd |D  |j}|j}| j}| |}	xX|D ]P}
|	|
||}t|
}
|rx2|D ] }t|}|| ||
|V  qW qp||
 qpW dS )z
		For internal use only.
		c             S   s   || fS )Nr   )abr   r   r   <lambda>      z-coincgen_doubles.doublesgen.<locals>.<lambda>c             S   s   | |fS )Nr   )rO   rP   r   r   r   rQ     rR   c             s   s   | ]}t |V  qd S )N)r)   )r*   r   r   r   r   r+     s    z.coincgen_doubles.doublesgen.<locals>.<genexpr>N)rE   r3   r,   adddiscardr@   rB   r)   )r   Zeventsar?   Zeventsbsingles_idsZunswapZsingles_ids_addZsingles_ids_discardr@   Zqueueb_get_coincsZeventamatchesZeventbr   r   r   
doublesgen  s(    




zcoincgen_doubles.doublesgenc             C   sR   t | j\}}| j| j| j| j }t | | j| ||| j| ||S )a  
		Generate a sequence of 2-element tuples of Python IDs of
		coincident event pairs.  Calling code is assumed to be in
		the process of constructing all possible n-tuple
		coincidences such that at least one time-shifted event in
		the n-tuple is before t, and so we construct and return all
		pairs required to decide that set, meaning we will
		construct and return pairs that might contain only events
		after t so that the calling code can test them for
		coincidence with events from other instruments that precede
		t.  The internal queues are flushed of events that cannot
		be used again assuming that t never goes backwards.  If t
		is the special value None then all coincident pairs that
		can be constructed from the internal queues are constructed
		and returned, the queues are emptied and returned to their
		initial state.

		The order of the IDs in each tuple is alphabetical by
		instrument.

		The Python IDs of events that are not reported in a
		coincident pair are placed in the singles_ids set.

		NOTE:  the singles_ids parameter passed to this function
		must a set or set-like object.  It must support Python set
		manipulation methods, like .clear(), .update(), and so on.
		)sortedrK   r   rW   r7   )r   r5   rU   ZinstrumentaZinstrumentbr?   r   r   r   r7     s    !zcoincgen_doubles.pullN)r8   r9   r:   r;   r   objectrB   r&   r=   rL   r'   r(   r1   rW   r7   r   r   r   r   r>   4  s   D#-r>   c               @   sJ   e Zd ZdddZedd Zedd Zedd	 Zd
d Z	dd Z
dS )TimeSlideGraphNodeNc                s   || _ || _t|  | _t|k| _t|dkrzt fdd| |t|d D | _	t
dd | j	D  | _ndt|dkr| f| _	| j	d j| _n:t|dkrֈ rtdf| _	| j	d j| _ntdtj| _jj| _d S )	NrC   c             3   s   | ]}t | V  qd S )N)rZ   )r*   Zcomponent_offset_vector)rM   coincgen_doubles_typemin_instrumentsr   r   r+   F  s    z.TimeSlideGraphNode.__init__.<locals>.<genexpr>r   c             s   s    | ]}|j jD ]
}|V  qqd S )N)r%   maps)r*   noder%   r   r   r   r+   O  s    r   g        zoffset_vector cannot be empty)time_slide_idrL   anyrH   Zis_zero_lagrE   keep_partialr2   component_offsetvectors
componentsr   r%   AssertionErrorr   r    r
   r"   
previous_tr   )r   r[   rL   rM   r\   r_   r   )rM   r[   r\   r   r&   6  s"    ,	
zTimeSlideGraphNode.__init__c             #   sR   d|  krt  ksn tx.t |D ]}t fdd|D V  q,W dS )a  
		Equivalent to offsetvector.component_offsetvectors() except
		only one input offsetvector is considered, not a sequence
		of them, and the output offsetvector objects preserve the
		absolute offsets of each instrument, not only the relative
		offsets between instruments.
		r   c             3   s   | ]}| | fV  qd S )Nr   )r*   rD   )rL   r   r   r+   s  s    z=TimeSlideGraphNode.component_offsetvectors.<locals>.<genexpr>N)rE   rd   	itertoolscombinationsr   )rL   nZselected_instrumentsr   )rL   r   rb   h  s    	z*TimeSlideGraphNode.component_offsetvectorsc             C   s   t dd | jD S )z0
		The earliest of the component nodes' .age.
		c             s   s   | ]}|j V  qd S )N)r'   )r*   r^   r   r   r   r+   z  s    z)TimeSlideGraphNode.age.<locals>.<genexpr>)rN   rc   )r   r   r   r   r'   u  s    zTimeSlideGraphNode.agec             C   s   t dd | jD S )z=
		The earliest of the component nodes' .t_coinc_complete.
		c             s   s   | ]}|j V  qd S )N)r(   )r*   r^   r   r   r   r+     s    z6TimeSlideGraphNode.t_coinc_complete.<locals>.<genexpr>)rN   rc   )r   r   r   r   r(   |  s    z#TimeSlideGraphNode.t_coinc_completec             C   sn   | j }t| jdkr<|f| j ks(t| jd || n(x&| jD ]}||jkrD|||| qDW | j |kS )a  
		Push new events from some instrument into the internal
		queues.  The iterable of events need not be time-ordered.
		With self.singlesqueue.event_time() defining the time of
		events, t_complete sets the time up-to which the collection
		of events is known to be complete.  That is, in the future
		no new events will be pushed whose times are earlier than
		t_complete.

		Returns True if this node's .t_coinc_complete property has
		been changed by this operation, False otherwise.  If
		.t_coinc_complete does not change, then no new candidates
		can yet be formed and it is not necessary to perform a
		coincidence analysis at this time.
		r   r   )r(   rE   rL   keysrd   rc   r1   )r   rD   r0   r#   Zt_beforer^   r   r   r   r1     s    
zTimeSlideGraphNode.pushc          	      sh   | j krt t fS  dk	r" ntj| _ t| jdkr`tdd | jd  d D t fS t| jdkrt }t| jd  |}|| j	rtdd |D nt fS t| jdkst
t fdd| jD }td	d |D }| j	r,ttj| }|d
d ttjdd |D   D  nt }~g }|d }|d }|d }	~x|D ]}
|t||
dd t||
dd |
d d f  }|	t|	|
dd t|	|
dd |
d d f  }x|D ]x}|
dd |dd  }t||}|t|k r|| |kr|
dd | }|t|t|d  || qW qXW |  t|}||fS )a  
		Using the events contained in the internal queues construct
		and return all coincidences they participate in.  It is
		assumed calling code is in the process of constructing
		n-tuple candidates such that each candidate contains at
		least one event preceding t, and so we construct and return
		all candidates required to decide that set, which might
		include candidates all of whose events come after t.

		Two objects are returned:  a tuple of the (n=N)-way
		coincidences, where N is the number of instruments in this
		node's offset vector, and a set of the
		(min_instruments<=n<N)-way coincidences, which will be
		empty if N < min_instruments.

		Since the (min_instruments<=n<N)-tuple coincidences cannot
		go on to form (n>N)-tuple coincidences, any that fail to
		contain at least one event preceding t could, at this stage,
		be discarded, however testing for that would require paying
		the price of the ID-->event look-up on all events in all
		candidates, which we will have to perform at each level of
		the graph, including the final stage before reporting
		candidates, so it is more efficient to leave the invalid
		candidates in the stream at this stage and cull them in a
		single pass at the end.

		The coincidences are reported as tuples of the Python IDs
		of the events that form coincidences.  The .index look-up
		table can be used to map these IDs back to the original
		events.  To do that, however, a copy of the contents of the
		.index mapping must be made before calling this method
		because this method will result in some of the events in
		question being removed from the queues, and thus also from
		the .index mapping.
		Nr   c             s   s   | ]}t |fV  qd S )N)r)   )r*   r   r   r   r   r+     s    z*TimeSlideGraphNode.pull.<locals>.<genexpr>r   rC   c             s   s   | ]}|fV  qd S )Nr   )r*   Zeventidr   r   r   r+     s    c             3   s   | ]}|  V  qd S )N)r7   )r*   	component)r5   r   r   r+     s    c             s   s   | ]}|d  V  qdS )r   Nr   )r*   elemr   r   r   r+   	  s    c             s   s   | ]\}}|d kr|V  qdS )rC   Nr   )r*   coinccountr   r   r   r+   3  s    c             s   s   | ]}|d  V  qdS )r   Nr   )r*   rk   r   r   r   r+   3  s    )re   r2   setr
   r"   rE   rL   rc   r7   ra   rd   rf   chainr,   r   rJ   r   difference_updaterg   appendr/   )r   r5   rU   r	   Z#component_coincs_and_partial_coincsZcomponent_coincsZpartial_coincsZcomponent_coincs0Zcomponent_coincs1Zcomponent_coincs2Zcoinc0Zcoincs1Zcoincs2Zcoinc1Zconfirmationr6   Z	new_coincr   )r5   r   r7     sF    )
	("!.
66

zTimeSlideGraphNode.pull)N)r8   r9   r:   r&   r<   rb   r=   r'   r(   r1   r7   r   r   r   r   rZ   5  s   
2rZ   c               @   s4   e Zd ZdddZedd Zdd Zdd
dZd	S )TimeSlideGraphrC   Fc                sH  dk rt d tdd | D k rPt dtt| dd dft fd	dttd
d | D dD |rtdd	dd 
 D   |rtdt| tjd tfddt|
 D | _tdd | jD  | _t | _t | _|rDfddtdttfdd| jD  tjd d S )Nr   z!require min_instruments >= 1 (%d)c             s   s   | ]}t |V  qd S )N)rE   )r*   rL   r   r   r   r+     s    z*TimeSlideGraph.__init__.<locals>.<genexpr>z@encountered offset vector (%s) smaller than min_instruments (%d)c             S   s   t | S )N)rE   )rL   r   r   r   rQ     rR   z)TimeSlideGraph.__init__.<locals>.<lambda>)keyc             3   s"   | ]}t | t|  fV  qd S )N)rF   r   )r*   pair)r@   r   r   r+     s    c             s   s   | ]}|D ]
}|V  q
qd S )Nr   )r*   rL   rD   r   r   r   r+     s    rC   zcoincidence windows:
	%sz,
	c             s   s(   | ] \}}d d t||f V  qdS )z	%s = %g s+N)joinrX   )r*   rv   dtr   r   r   r+     s    zAconstructing coincidence assembly graph for %d offset vectors ...)filec             3   s$   | ]\}}t | |d V  qdS ))r_   N)rZ   )r*   r_   rL   )rM   r[   r\   r   r   r+     s   c             s   s   | ]}|j V  qd S )N)r%   )r*   r^   r   r   r   r+     s    c                s:   t | jdkrtdS tdt fdd| jD  S )Nr   )r   r   )r   r   c             3   s   | ]} |V  qd S )Nr   )r*   r^   )walkr   r   r+     s    z8TimeSlideGraph.__init__.<locals>.walk.<locals>.<genexpr>)rE   rc   numpyarrayr   )r^   )r{   r   r   r{     s    z%TimeSlideGraph.__init__.<locals>.walkz:graph contains %d fundamental nodes, %d higher-order nodesc             3   s   | ]} |V  qd S )Nr   )r*   r^   )r{   r   r   r+     s    )r    rN   rH   strrI   rf   rg   rp   printrx   rJ   rE   sysstderrr2   rX   headr   r%   used_idsreported_idsr   )r   r[   Zoffset_vector_dictr@   r\   verboser   )r@   rM   r[   r\   r{   r   r&     s$    "0 

zTimeSlideGraph.__init__c             C   s   t dd | jD S )z3
		The earliest of the graph's head nodes' .age.
		c             s   s   | ]}|j V  qd S )N)r'   )r*   r^   r   r   r   r+     s    z%TimeSlideGraph.age.<locals>.<genexpr>)rN   r   )r   r   r   r   r'     s    zTimeSlideGraph.agec                s   t  fdd| jD S )aL  
		Push new events from some instrument into the internal
		queues.  The iterable of events need not be time-ordered.
		With self.singlesqueue.event_time() defining the time of
		events, t_complete sets the time up-to which the event
		stream from this instrument is known to be complete.  That
		is, in the future no new events will be pushed from this
		instrument whose times are earlier than t_complete.
		t_complete is measured with respect to the unshifted times
		of the events.

		Returns True if the graph's .t_coinc_complete has changed,
		indicating that it might be possible to form new
		candidates;  False if the addition of these events does not
		yet allow new candidates to be formed.  If the return value
		is False, .pull() is guaranteed to return an empty
		candidate list unless the graph is flushed of all
		candidates.
		c                s$   g | ]}|j kr| qS r   )rL   r1   )r*   r^   )r0   rD   r#   r   r   
<listcomp>  s    z'TimeSlideGraph.push.<locals>.<listcomp>)r`   r   )r   rD   r0   r#   r   )r0   rD   r#   r   r1     s    zTimeSlideGraph.pushNc             #   s  t | j |d krdd }|d kr0dd }n|j}t }t}	|j}
| jj}xt| jddD ]\}}|j	 |j
|rd }t|jtj}n|j}t|j|}xztj|| D ]f}tfdd|D }t fdd|D |krq|| || ||s|
| ||fV  qW qbW |rB|| j8 }|  j|O  _|	t| j8 }	|	r~|	| j }|  j|	8  _|  j|	8  _nt }|r| js| jrt|d k	rfd	d|D |d d < |d k	rfd
d|	D |d d < |d k	r
fdd|D |d d < d S )Nc             S   s   dS )NFr   )r0   rL   r   r   r   rQ     rR   z%TimeSlideGraph.pull.<locals>.<lambda>c             S   s   d S )Nr   )	event_idsrL   r   r   r   rQ      rR   r   )startc             3   s   | ]} | V  qd S )Nr   )r*   event_id)r%   r   r   r+     s    z&TimeSlideGraph.pull.<locals>.<genexpr>c             3   s    | ]} ||j   V  qd S )N)Zifo)r*   r   )r   rL   r   r   r+      s    c             3   s   | ]} | V  qd S )Nr   )r*   r   )r%   r   r   r+   E  s    c             3   s   | ]} | V  qd S )Nr   )r*   r   )r%   r   r   r+   G  s    c             3   s   | ]} | V  qd S )Nr   )r*   r   )r%   r   r   r+   I  s    )rI   r%   rJ   r1   rp   r,   r   	enumerater   r   rL   r
   segmentre   ZPosInfinityr(   rf   rq   r7   r2   rN   r   rd   )r   Znewly_reportedflushedZflushed_unusedflushZcoinc_sieveZevent_collectorZevent_collector_pushZnewly_reported_idsZflushed_idsZnewly_reported_updateZused_updaterh   r^   r5   Zcandidate_segr   r0   Zflushed_unused_idsr   )r   r%   rL   r   r7     sV    








zTimeSlideGraph.pull)rC   F)NNNFNN)r8   r9   r:   r&   r=   r'   r1   r7   r   r   r   r   rt     s   
Ort   c               @   s(   e Zd ZdZdd Zdd Zdd ZdS )	CoincTableszx
	A convenience interface to the XML document's coincidence tables,
	allowing for easy addition of coincidence events.
	c             C   s   yt j|| _W n4 tk
rF   t t j| _|jd | j Y nX | j  yt j	|| _
W n4 tk
r   t t j	| _
|jd | j
 Y nX tj||j|jd|jd| _t j|| _| j | _d S )Nr   T)Z
create_newdescription)r   Z
CoincTableZ	get_table
coinctabler    ZNewZ
childNodesZappendChildZsync_next_idZCoincMapTablecoincmaptableligolw_coincsZget_coinc_def_idsearchZsearch_coinc_typer   coinc_def_idZTimeSlideTableZtime_slide_tableas_dictZtime_slide_index)r   ZxmldocZcoinc_definer_rowr   r   r   r&   Z  s    
zCoincTables.__init__c          	      sH    fdd|D }|s t d jj| jd|dt|dd}||fS )a  
		From a process ID, a time slide ID, and a sequence of
		events (generator expressions are OK), constructs and
		initializes a coinc_event table row object and a sequence
		of coinc_event_map table row objects describing the
		coincident event.  The return value is the coinc_event row
		and a sequence of the coinc_event_map rows.

		The coinc_event is *not* assigned a coinc_event_id by this
		method.  It is expected that will be done in
		.append_coinc().  This allows sub-classes to defer the
		question of whether or not to include the coincidence in
		the search results without consuming additional IDs.

		The coinc_event row's .instruments and .likelihood
		attributes are initialized to null values.  The calling
		code should populate as needed.

		When subclassing this method, if the time shifts that were
		applied to the events in constructing the coincidence are
		required to compute additional metadata, they can be
		retrieved from self.time_slide_index using the
		time_slide_id.
		c                s    g | ]} j jd |jdqS )N)coinc_event_id
table_namer   )r   RowTyper   )r*   r   )r   r   r   r   r     s   z*CoincTables.coinc_rows.<locals>.<listcomp>zcoincs must contain >= 1 eventN)
process_idr   r   r_   ZinstsZneventsZ
likelihood)rd   r   r   r   rE   )r   r   r_   r0   r   Z	coincmapsrl   r   )r   r   r   
coinc_rowsq  s    zCoincTables.coinc_rowsc             C   s>   | j  |_| j | x |D ]}|j|_| j| qW |S )z
		Appends the coinc_event row object and coinc_event_map row
		objects to the coinc_event and coinc_event_map tables
		respectively after assigning a coinc_event_id to the
		coincidence.  Returns the coinc_event row object.
		)r   Zget_next_idr   rs   r   )r   Zcoinc_event_rowZcoinc_event_map_rowsrowr   r   r   append_coinc  s    
zCoincTables.append_coincN)r8   r9   r:   r;   r&   r   r   r   r   r   r   r   U  s   ,r   c               @   sR   e Zd ZdddZedd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd ZdS )
CoincRatesrC   -C6?c          
      sz  t _|_|_jtjkr.tdjdk r@td|dkrPtdtfddtt	jdD _
i _xjD ]}tfdd	d
 t	t g sdj|< qtdkrdj
t  d f  j|< qt}tj||d  |d fdd}xjtD ]^\}}	|}
|d9 }d|||
f< d||d |
f< j
t  |	f   ||df< ||d df< q&W xtttd|D ]\}\\}}\}}|d9 }d|||f< d|||f< d||d |f< d||d |f< j
t ||f   ||df< ||d df< qW tjtfdd}y tt||jjj|< W q tk
rp   Y nX qt	 fddD }t	fddtttdD }tj}tjd| }d\}}xV|||| kr&t	fdd|D tfdd|D r|d7 }|d7 }qW t|t| j|< x2D ]*}	j|  dj
t  |	f  9  < qDW qW dS )a  
		Model for coincidences among noise events collected from
		several antennas.  Independent Poisson processes are
		assumed.  The coincidence window for each pair of
		instruments is (delta_t + light travel time between that
		pair).  A coincidence is assumed to require full N-way
		coincidence and require at least min_instruments to
		participate.

		Initial configuration requires some relatively expensive
		pre-calculation of internal quantities, but once
		initialized coincidence rates can be computed quickly from
		observed event rates.  Several other related quantities can
		be computed.

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		z+require len(instruments) >= min_instrumentsg        zrequire delta_t >= 0.z#require abundance_rel_accuracy > 0.c             3   s$   | ]}t | jt|  fV  qd S )N)rF   delta_tr   )r*   ab)r   r   r   r+     s    z&CoincRates.__init__.<locals>.<genexpr>rC   c                s"   t  fddt g D S )Nc             3   s&   | ]}t jt |f V  qd S )N)r   logtaurF   )r*   rP   )rO   r   r   r   r+     s    z8CoincRates.__init__.<locals>.<lambda>.<locals>.<genexpr>)r   rp   )rO   )instrumentsr   )rO   r   rQ     rR   z%CoincRates.__init__.<locals>.<lambda>)ru   g      ?r   g       @r   double)Zdtypeg      rn   c             3   s6   | ].}j t |f  j t |f 
 fV  qd S )N)r   rF   )r*   rD   )anchorr   r   r   r+   U  s    c             3   s2   | ]*\}}||j t |  | f fV  qd S )N)r   rF   )r*   r6   j)r   r   r   r   r+   [  s    )r   r   c             3   s   | ]} | V  qd S )Nr   )r*   Zwindow)random_uniformr   r   r+   {  s    c             3   s,   | ]$\}}}t  |  |  |kV  qd S )N)abs)r*   r6   r   maxdt)ry   r   r   r+   |  s    N)rF   r   r   r\   rE   r    rI   rf   rg   r2   r   rate_factorsall_instrument_combosrN   rp   r|   zerosr   r   Z
ConvexHullZHalfspaceIntersectionZintersectionsvolumeAttributeErrorranger   r   randomuniformallfloat)r   r   r   r\   Zabundance_rel_accuracyru   
dimensionsZ
halfspacesr6   rD   r   Zj1rO   Zj2rP   ZinteriorwindowsijseqZ	math_sqrtZtwo_epsilonrh   dr   )r   ry   r   r   r   r   r&     sl    

&;"2,2 (
zCoincRates.__init__c                s0   t | j t  fddt| jt d D S )a.  
		A tuple of all possible instrument combinations (as
		frozensets).

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 1)
		>>> coincrates.all_instrument_combos
		(frozenset(['V1']), frozenset(['H1']), frozenset(['L1']), frozenset(['V1', 'H1']), frozenset(['V1', 'L1']), frozenset(['H1', 'L1']), frozenset(['V1', 'H1', 'L1']))
		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		>>> coincrates.all_instrument_combos
		(frozenset(['V1', 'H1']), frozenset(['V1', 'L1']), frozenset(['H1', 'L1']), frozenset(['V1', 'H1', 'L1']))
		c             3   s(   | ] }t  |D ]}t|V  qqd S )N)rf   rg   rF   )r*   rh   r   )all_instrumentsr   r   r+     s    z3CoincRates.all_instrument_combos.<locals>.<genexpr>r   )r2   r   r   r\   rE   )r   r   )r   r   r     s    
z CoincRates.all_instrument_combosc             K   s   t || jkr4tddt| jdt|f tdd | D rRtd| jrt| t| j  dkrtd|t| j f t	| j
}x.|D ]&}x |D ]}||  || 9  < qW qW |S )a`  
		Given the event rates for a collection of instruments,
		compute the rates at which N-way coincidences occur among
		them where N >= min_instruments.  The return value is a
		dictionary whose keys are frozensets of instruments and
		whose values are the rate of coincidences for that set.

		NOTE:  the computed rates are the rates at which
		coincidences among at least those instruments occur, not
		the rate at which coincidences among exactly those
		instruments occur.  e.g., considering the H1, L1, V1
		network, for the pair H1, L1 the return value is the sum of
		the rate at which those two instruments form double
		coincidences and also the rate at which they participate in
		H1, L1, V1 triple coincidences.

		See also .strict_coinc_rates().

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		>>> coincrates.coinc_rates(H1 = 0.001, L1 = 0.002, V1 = 0.003)
		{frozenset(['V1', 'H1']): 1.9372787960306537e-07, frozenset(['V1', 'H1', 'L1']): 1.0125819710267318e-11, frozenset(['H1', 'L1']): 6.00513846088957e-08, frozenset(['V1', 'L1']): 3.77380092200718e-07}
		>>> coincrates.coinc_rates(H1 = 0.001, L1 = 0.002, V1 = 0.002)
		{frozenset(['V1', 'H1']): 1.291519197353769e-07, frozenset(['V1', 'H1', 'L1']): 6.750546473511545e-12, frozenset(['H1', 'L1']): 6.00513846088957e-08, frozenset(['V1', 'L1']): 2.5158672813381197e-07}
		>>> coincrates.coinc_rates(H1 = 0.001, L1 = 0.002, V1 = 0.001)
		{frozenset(['V1', 'H1']): 6.457595986768845e-08, frozenset(['V1', 'H1', 'L1']): 3.3752732367557724e-12, frozenset(['H1', 'L1']): 6.00513846088957e-08, frozenset(['V1', 'L1']): 1.2579336406690598e-07}
		z,require event rates for %s, got rates for %sz, c             s   s   | ]}|d k V  qdS )g        Nr   )r*   rater   r   r   r+     s    z)CoincRates.coinc_rates.<locals>.<genexpr>zrates must be >= 0g      ?zGevents per coincidence window must be << 1: rates = %s, max window = %g)rp   r   r    rx   rX   r`   rH   r   rG   rI   r   )r   ratescoinc_ratesr   rD   r   r   r   r     s    &$


zCoincRates.coinc_ratesc             K   s|   | j f |}xHt|ddd dD ]2}x,| D ] \}}||k r.||  |8  < q.W q W tdd | D sxtd| |S )an  
		Given the event rates for a collection of instruments,
		compute the rates at which strict N-way coincidences occur
		among them where N >= min_instruments.  The return value is
		a dictionary whose keys are frozensets of instruments and
		whose values are the rate of coincidences for that set.

		NOTE:  the computed rates are the rates at which
		coincidences occur among exactly those instrument
		combinations, excluding the rate at which each combination
		participates in higher-order coincs.  e.g., considering the
		H1, L1, V1 network, for the pair H1, L1 the return value is
		the rate at which H1, L1 doubles occur, not including the
		rate at which the H1, L1 pair participates in H1, L1, V1
		triples.

		See also .coinc_rates().

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		>>> coincrates.strict_coinc_rates(H1 = 0.001, L1 = 0.002, V1 = 0.003)
		{frozenset(['V1', 'H1']): 1.937177537833551e-07, frozenset(['V1', 'H1', 'L1']): 1.0125819710267318e-11, frozenset(['H1', 'L1']): 6.004125878918543e-08, frozenset(['V1', 'L1']): 3.7736996638100773e-07}
		>>> coincrates.strict_coinc_rates(H1 = 0.001, L1 = 0.002, V1 = 0.002)
		{frozenset(['V1', 'H1']): 1.2914516918890337e-07, frozenset(['V1', 'H1', 'L1']): 6.750546473511545e-12, frozenset(['H1', 'L1']): 6.004463406242219e-08, frozenset(['V1', 'L1']): 2.5157997758733847e-07}
		>>> coincrates.strict_coinc_rates(H1 = 0.001, L1 = 0.002, V1 = 0.001)
		{frozenset(['V1', 'H1']): 6.457258459445168e-08, frozenset(['V1', 'H1', 'L1']): 3.3752732367557724e-12, frozenset(['H1', 'L1']): 6.004800933565894e-08, frozenset(['V1', 'L1']): 1.2578998879366924e-07}
		Tc             S   s   t | S )N)rE   )r   r   r   r   rQ     rR   z/CoincRates.strict_coinc_rates.<locals>.<lambda>)reverseru   c             s   s   | ]}|d kV  qdS )g        Nr   )r*   r   r   r   r   r+     s    z0CoincRates.strict_coinc_rates.<locals>.<genexpr>zencountered negative rate: %s)r   rX   rJ   r   rH   rd   )r   r   strict_coinc_ratesr   ru   r   r   r   r   r     s    "zCoincRates.strict_coinc_ratesc                s   t jkr4tddtjdtf tfddjD }t|d}x\| D ]P\ }xFj	f t fdd| D  D ]\}}||  || 7  < qW qdW |S )z
		A dictionary mapping instrument combination (as a
		frozenset) to the total number of coincidences involving
		precisely that combination of instruments expected from the
		background.
		z#require segmentlists for %s, got %sz, c          	   3   s4   | ],}|t t | j|  fV  qd S )N)r   r   intersectionunionr   )r*   on_instruments)seglistsr   r   r   r+   
  s    z>CoincRates.marginalized_strict_coinc_counts.<locals>.<genexpr>g        c             3   s&   | ]\}}|| kr|nd fV  qdS )g        Nr   )r*   rD   r   )r   r   r   r+     s    )
rp   r   r    rx   rX   rI   r   fromkeysrJ   r   )r   r   r   ZlivetimeZcoinc_countTZcoinc_instrumentsr   r   )r   r   r   r    marginalized_strict_coinc_counts  s    &0z+CoincRates.marginalized_strict_coinc_countsc                s   | j f |}t| dkr(tdtfdd| D }tt|  td  dk sftt fdd| D S )a  
		Given the event rates for a collection of instruments,
		compute the natural logarithm of the probability that a
		coincidence is found to involve exactly a given set of
		instruments.  This is equivalent to the ratios of the
		values in the dictionary returned by .strict_coinc_rates()
		to their sum.

		Raises ZeroDivisionError if all coincidence rates are 0.

		See also .strict_coinc_rates().

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		>>> coincrates.lnP_instruments(H1 = 0.001, L1 = 0.002, V1 = 0.003)
		{frozenset(['V1', 'H1']): -1.181124067253893, frozenset(['V1', 'H1', 'L1']): -11.040192999777876, frozenset(['H1', 'L1']): -2.352494317162074, frozenset(['V1', 'L1']): -0.5143002401188091}
		g        zall rates are 0c             3   s   | ]\}}||  fV  qd S )Nr   )r*   r   r   )
total_rater   r   r+   +  s    z-CoincRates.lnP_instruments.<locals>.<genexpr>g      ?g+=c             3   s,   | ]$\}}||rt |  ntfV  qd S )N)r   r   r   )r*   r   P)normr   r   r+   /  s    )	r   r   rH   ZeroDivisionErrorrI   rJ   rX   r   rd   )r   r   r   ZP_instrumentsr   )r   r   r   lnP_instruments  s    zCoincRates.lnP_instrumentsc             +   sN   | j f |}tt| t fdd| D }tj}x||V  q<W dS )ar  
		Generator that, given the event rates for a collection of
		instruments, yields a sequence of two-element tuples each
		containing a randomly-selected frozen set of instruments
		and the natural logarithm of the ratio of the rate at which
		that combination of instruments forms coincidences to the
		rate at which it is being yielded by this generator.

		See also .lnP_instruments().

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		>>> x = iter(coincrates.random_instruments(H1 = 0.001, L1 = 0.002, V1 = 0.003))
		>>> x.next()	# doctest: +SKIP
		(frozenset(['H1', 'L1']), -3.738788683913535)
		c             3   s   | ]\}}||  fV  qd S )Nr   )r*   r   ZlnP)lnNr   r   r+   G  s    z0CoincRates.random_instruments.<locals>.<genexpr>N)r   r   r   rE   r2   rJ   r   choice)r   r   r   resultsr   r   )r   r   random_instruments2  s    zCoincRates.random_instrumentsc             #   s  t tjkr6tddttj  tjk rZtdjtf d dd    dff}tj	t  fdd	D }t fd
d	t
ttdD }x>t fdd	|D tfdd	|D rt| V  qW dS )a   
		Generator that yields dictionaries of random event
		time-of-arrival offsets for the given instruments such that
		the time-of-arrivals are mutually coincident given the
		maximum allowed inter-instrument \Delta t's.  The values
		returned are offsets, and would need to be added to some
		common time to yield absolute arrival times.

		Example:

		>>> coincrates = CoincRates(("H1", "L1", "V1"), 0.005, 2)
		>>> x = iter(coincrates.plausible_toas(("H1", "L1")))
		>>> x.next()	# doctest: +SKIP
		{'H1': 0.0, 'L1': -0.010229226372297711}
		znot configured for %sz, z'require at least %d instruments, got %dr   r   Ng        c             3   s8   | ]0}|j t |f  j t |f 
 fV  qd S )N)r   rF   )r*   rD   )r   r   r   r   r+   e  s    z,CoincRates.plausible_toas.<locals>.<genexpr>c             3   s2   | ]*\}}||j t |  | f fV  qd S )N)r   rF   )r*   r6   r   )r   r   r   r   r+   f  s    rC   c             3   s"   | ]\}}}| ||fV  qd S )Nr   )r*   rD   lohi)r   r   r   r+   h  s    c             3   s4   | ],\}}}t  | d   | d   |kV  qdS )r   N)r   )r*   r6   r   r   )ry   r   r   r+   i  s    )r2   rp   r   r    rx   rX   rE   r\   r   r   rf   rg   r   r   rI   )r   r   Zanchor_offsetr   r   r   )r   ry   r   r   r   r   plausible_toasM  s     
(zCoincRates.plausible_toasN)rC   r   )r8   r9   r:   r&   r=   r   r   r   r   r   r   r   r   r   r   r   r     s   
 Q60r   c               @   s2   e Zd ZdZejfddZdd Zedd Z	dS )	TOATriangulatorax  
	Time-of-arrival triangulator.  See section 6.6.4 of
	"Gravitational-Wave Physics and Astronomy" by Creighton and
	Anderson.

	An instance of this class is a function-like object that accepts a
	tuple of event arival times and returns a tuple providing
	information derived by solving for the maximum-likelihood source
	location assuming Gaussian-distributed timing errors.
	c             C   s"  t |t |kstt |dks$tt|| _t|| _|| _t| j| jddtj	f d  td| jd   }| j| | _
| j
| j| jddtj	f   }tj|\| _| _| _t |dkrt| j | j  dk | _n8t| jd | jd  | jd | jd  d | j | _dS )a  
		Create and initialize a triangulator object.

		rs is a sequence of location 3-vectors, sigmas is a
		sequence of the timing uncertainties for those locations.
		Both sequences must be in the same order --- the first
		sigma in the sequence is interpreted as belonging to the
		first location 3-vector --- and, of course, they must be
		the same length.

		v is the speed at which the wave carrying the signals
		travels.  The rs 3-vectors carry units of distance, the
		sigmas carry units of time, v carries units of
		distance/time.  What units are used for the three is
		arbitrary, but they must be mutually consistent.  The
		default value for v in c, the speed of light, in
		metres/second, therefore the location 3-vectors should be
		given in metres and the sigmas should be given in seconds
		unless a value for v is provided with different units.

		Example:

		>>> from numpy import array
		>>> triangulator = TOATriangulator([
		...	array([-2161414.92636, -3834695.17889, 4600350.22664]),
		...	array([  -74276.0447238, -5496283.71971  ,  3224257.01744  ]),
		...	array([ 4546374.099   ,   842989.697626,  4378576.96241 ])
		... ], [
		...	0.005,
		...	0.005,
		...	0.005
		... ])
		...
		>>>

		This creates a TOATriangulator instance configured for the
		LIGO Hanford, LIGO Livingston and Virgo antennas with 5 ms
		time-of-arrival uncertainties at each location.

		Note:  rs and sigmas may be iterated over multiple times.
		rC   Nr      g:0yE>r   g      ?)rE   rd   r|   Zvstackrsr}   sigmasvr   ZnewaxisRZlinalgZsvdUSVTr   rN   rG   singulardotZmax_dt)r   r   r   r   ZrbarMr   r   r   r&     s    *4  zTOATriangulator.__init__c                s  t |t | jkstt|tfdd|D }t|| jd  td| jd   }|| | j }t| jj	|dd }t | j
dkr| jrrt|| j || j g}yjtd|d d d  |d d d  |d d< td|d d d  |d d d   |d d< W n> tk
rZ   d	|j	d< |tt|d |d  }Y nX ttdtdg}t| jj	|j	j	}tt|d |d d d
k sttt|d |d d d
k stt|t| j
|d | j  | jd  td| jd   }tt| j|d | j| j  | d }|| t| j
|d | j  }	tt|	|	}	q| j| | j| j fdd  fdd}
| j | j }|d }d}x*|
||
| dkr||d  }}qW tj|
||} |}t| jj	|}tt||d d
k s&tt|t| j
|| j  | jd  td| jd   }tt| j|| j| j  | d }|| t| j
|| j  }	tt|	|	}	n|d | jd  }y"t|dtd|d  g}W n& tk
r   t|ddg}Y nX t| jj	|}tt||d d
k s@tt|t| j
|| j  | jd  td| jd   }tt| j|| j| j  | d }|| t| j
|| j  }	tt|	|	}	d}|| |t | j |	fS )a  
		Triangulate the direction to the source of a signal based
		on a tuple of times when the signal was observed.  ts is a
		sequence of signal arrival times.  One arrival time must be
		provided for each of the observation locations provided
		when the instance was created, and the units of the arrival
		times must be the same as the units used for the sequence
		of sigmas.

		The return value is a tuple of information derived by
		solving for the maximum-likelihood source location assuming
		Gaussian-distributed timing errors.  The return value is

			(n, toa, chi2 / DOF, dt)

		where n is a unit 3-vector pointing from the co-ordinate
		origin towards the source of the signal, toa is the
		time-of-arrival of the signal at the co-ordinate origin,
		chi2 / DOF is the \chi^{2} per degree-of-freedom from to
		the arrival time residuals, and dt is the root-sum-square
		of the arrival time residuals.

		Example:

		>>> from numpy import array
		>>> from numpy import testing
		>>> triangulator = TOATriangulator([
		...	array([-2161414.92636, -3834695.17889, 4600350.22664]),
		...	array([  -74276.0447238, -5496283.71971  ,  3224257.01744  ]),
		...	array([ 4546374.099   ,   842989.697626,  4378576.96241 ])
		... ], [
		...	0.005,
		...	0.005,
		...	0.005
		... ])
		...
		>>> n, toa, chi2_per_dof, dt = triangulator([
		...	794546669.429688,
		...	794546669.41333,
		...	794546669.431885
		... ])
		...
		>>> n
		array([[-0.45605637,  0.75800934,  0.46629865],
		       [-0.45605637,  0.75800934,  0.46629865]])
		>>> testing.assert_approx_equal(toa, 794546669.4269662)
		>>> testing.assert_approx_equal(chi2_per_dof, 0.47941941158371465)
		>>> testing.assert_approx_equal(dt, 0.005996370224459011)

		NOTE: if len(rs) >= 4, n is a 1x3 array.
		      if len(rs) == 3, n is a 2x3 array.
		      if len(rs) == 2, n is None.
		NOTE: n is not the source direction but the propagation
		      direction of GW.
		      Therefore, if you want source direction, you have to
		      multiply -1.
		NOTE: n is represented by earth fixed coordinate, not
		      celestial coordinate.
		      Up to your purpose, you should transform \phi -> RA.
		      To do it, you can use dir2coord.
		c                s   g | ]}t |  qS r   )r   )r*   r5   )t0r   r   r     s    z,TOATriangulator.__call__.<locals>.<listcomp>rC   r   Nr   g      ?r   g        g:0yE>c             S   s   |||   S )Nr   )lZ	StauprimeZS2r   r   r   n_prime/  s    z)TOATriangulator.__call__.<locals>.n_primec                s    | }t ||d S )Nr   )r|   r   )r   np)r   r   r   secular_equation1  s    z2TOATriangulator.__call__.<locals>.secular_equationrn   )rE   r   rd   rN   r|   r}   r   r   r   r   r   r   r   r   r   r    r   r   r   r   r   scipyoptimizeZbrentq)r   tsZtbarr   Z	tau_primer   rh   ZtoaZchi2ry   r   ZlsingZl_loZl_hir   Zxpr   )r   r   r   rA     sf    >$28
$&&<* 
8&"8&zTOATriangulator.__call__c             C   sd   t | dkrtd| tt| |  } |  } t| d | d t| }t	| d }||fS )a  
		This transforms from propagation direction vector to right
		ascension and declination source co-ordinates.

		The input is the propagation vector, n, in Earth fixed
		co-ordinates (x axis through Greenwich merdian and equator,
		z axis through North Pole), and the time.  n does not need
		to be a unit vector.  The return value is the (ra, dec)
		pair, in radians.  NOTE:  right ascension is not
		necessarily in [0, 2\pi).
		r   zn must be a 3-vectorr   r   rC   )
rE   r    r   r   r|   r   Zarctan2r   ZGreenwichMeanSiderealTimeZarcsin)rh   ZgpsZRAZDECr   r   r   	dir2coord  s    zTOATriangulator.dir2coordN)
r8   r9   r:   r;   r   r   r&   rA   r<   r   r   r   r   r   r   v  s
   
D >r   c               @   s`   e Zd ZdZdd Zdd Zdd Zdd	 Zd
d Zdd Z	dd Z
edd Zedd ZdS )LnLRDensitya  
	Base class for parameter distribution densities for use in log
	likelihood ratio ranking statistics.  Generally several instances
	of (subclasses of) this will be grouped together to construct a log
	likelihood ratio class for use as a ranking statistic in a
	trigger-based search.  For example, as a minimum one would expect
	one instance for the numerator and another for the denominator, but
	additional ones might be included in a practical ranking statistic
	implementation, for example a third might be used for storing a
	histogram of the candidates observed in a search.

	Typically, the ranking statistic implementation will provide a
	function to transform a candidate to the arguments to use with the
	.__call__() implementation, and so in this way a LnLRDensity object
	is generally only meaningful in the context of the ranking
	statistic class for which it has been constructed.
	c             O   s   t dS )za
		Evaluate.  Return the natural logarithm of the density
		evaluated at the given parameters.
		N)r   )r   argskwargsr   r   r   rA     s    zLnLRDensity.__call__c             C   s   t dS )z$
		Marginalize the two densities.
		N)r   )r   otherr   r   r   __iadd__  s    zLnLRDensity.__iadd__c             O   s   t dS )zK
		Increment the counts defining this density at the given
		parameters.
		N)r   )r   r   r   r   r   r   	increment  s    zLnLRDensity.incrementc             C   s   t dS )z-
		Return a duplicate copy of this object.
		N)r   )r   r   r   r   copy  s    zLnLRDensity.copyc             C   s   t dS )aB  
		Ensure all internal densities are normalized, and
		initialize interpolator objects as needed for smooth
		evaluation.  Must be invoked before .__call__() will yield
		sensible results.

		NOTE:  for some implementations this operation will
		irreversibly alter the contents of the counts array, for
		example often this operation will involve the convolution
		of the counts with a density estimation kernel.  If it is
		necessary to preserve a pristine copy of the counts data,
		use the .copy() method to obtain a copy of the data, first,
		and then .finish() the copy.
		N)r   )r   r   r   r   finish  s    zLnLRDensity.finishc             C   s   t dS )a+  
		Generator returning a sequence of parameter values drawn
		from the distribution density.  Some subclasses might
		choose not to implement this, and those that do might
		choose to use an MCMC-style sample generator and so the
		samples should not be assumed to be statistically
		independent.
		N)r   )r   r   r   r   samples  s    	zLnLRDensity.samplesc             C   s   t dd| iS )z
		Serialize to an XML fragment and return the root element of
		the resulting XML tree.

		Subclasses must chain to this method, then customize the
		return value as needed.
		Namez%s:lnlrdensity)r   LIGO_LW)r   namer   r   r   to_xml  s    zLnLRDensity.to_xmlc                sL   d    fdd| tjjD }t|dkrDtdtjj f |d S )z
		Sub-classes can use this in their overrides of the
		.from_xml() method to find the root element of the XML
		serialization.
		z%s:lnlrdensityc                s$   g | ]}| d r|j kr|qS )r   )ZhasAttributer   )r*   rk   )r   r   r   r   
	  s    z,LnLRDensity.get_xml_root.<locals>.<listcomp>r   z5XML tree must contain exactly one %s element named %sr   )ZgetElementsByTagNamer   r   ZtagNamerE   r    )clsxmlr   r   )r   r   get_xml_root	  s
    zLnLRDensity.get_xml_rootc             C   s   t dS )z
		In the XML document tree rooted at xml, search for the
		serialized LnLRDensity object named name, and deserialize
		it.  The return value is the deserialized LnLRDensity
		object.
		N)r   )r   r   r   r   r   r   from_xml	  s    zLnLRDensity.from_xmlN)r8   r9   r:   r;   rA   r   r   r   r   r   r   classmethodr   r   r   r   r   r   r     s   
r   c               @   s"   e Zd ZdZdd ZdddZdS )LnLikelihoodRatioMixinaC  
	Mixin to assist in implementing a log likelihood ratio ranking
	statistic class.  The ranking statistic class that inherits from
	this must:  (i) define a callable .numerator attribute that returns
	ln P(*args, **kwargs | signal);  (ii) define a callable
	.denominator attribute that returns ln P(*args, **kwargs | noise).

	Inheriting from this will:

	1.  Add a .__call__() method that returns the natural logarithm of
	the likelihood ratio

	ln P(*args, **kwargs | signal) - ln P(*args, **kwargs | noise)

	The implementation handles various special cases sensibly, such as
	when either or both of the logarithms of the numerator and
	denominator diverge.

	2.  Add a .ln_lr_samples() method that makes use of the .numerator
	and .denominator attributes, together with the .__call__() method
	to transform a sequence of (*args, **kwargs) into a sequence of log
	likelihood ratios and their respective relative frequencies.  This
	can be used to construct histograms of P(ln L | signal) and P(ln L
	| noise).  These distributions are required for, for example,
	signal rate estimation and false-alarm probability estimation.

	Why is the likelihood ratio useful?  Starting from Bayes' theorem,
	and using the fact that "signal" and "noise" are the only two
	choices:

	                   P(data | signal) * P(signal)
	P(signal | data) = ----------------------------
	                              P(data)

	                  P(data | signal) * P(signal)
	  = ---------------------------------------------------------
	    P(data | noise) * P(noise) + P(data | signal) * P(signal)

	            [P(data | signal) / P(data | noise)] * P(signal)
	  = ----------------------------------------------------------------
	    1 - P(signal) + [P(data | signal) / P(data | noise)] * P(signal)

	                        Lambda * P(signal)
	P(signal | data) = ----------------------------
	                   1 + (Lambda - 1) * P(signal)

	               P(data | signal)
	where Lambda = ----------------
	               P(data | noise)

	Differentiating P(signal | data) w.r.t. Lambda shows the derivative
	is always positive, so the probability that a candidate is the
	result of a gravitiational wave is a monotonically increasing
	function of the likelihood ratio.  Ranking events from "most likely
	to be a genuine signal" to "least likely to be a genuine signal"
	can be performed by sorting candidates by likelihood ratio.  Or, if
	one wanted to set a threshold on P(signal | data) to select a
	subset of candidates such that the candidates selected have a given
	purity (the fraction of them that are real is fixed), that is
	equivalent to selecting candidates using a likelihood ratio
	threshold.

	These are expressions of the Neyman-Pearson lemma which tells us
	that thresholding on Lambda creates a detector that extremizes the
	detection efficiency at fixed false-alarm rate.
	c             O   sb   | j ||}| j||}t|rZt|rZ|dk r@|dk r@tS |dkrZ|dkrZtd || S )a  
		Return the natural logarithm of the likelihood ratio for
		the given parameters,

		ln P(*args, **kwargs | signal) - ln P(*args, **kwargs | noise)

		The arguments are passed verbatim to the .__call__()
		methods of the .numerator and .denominator attributes of
		self and the return value is computed from the results.

		NOTE:  sub-classes may override this method, possibly
		chaining to it if they wish.  The .ln_lr_samples()
		mechanism does not require this method to return exactly
		the natural logarithm of the .numerator/.denominator ratio.
		The .ln_lr_samples() mechanism does not assume the
		numerator, denominator and ranking statistic are related to
		each other as the latter being the ratio of the former two,
		it evaluates all three separately.  For this reason, the
		.__call__() method that implements the ranking statistic is
		free to include other logic, such as hierarchical cuts or
		bail-outs that are not stricly equivalent to the ratio of
		the numerator and denominator.

		Special cases:

		.numerator/.denominator=0/0 is mapped to ln Lambda = -inf,
		meaning P(signal | data) = 0.  Although this condition is
		nonsensical because the data is claimed to be inconsistent
		with both noise and signal --- the only two things it can
		be --- our task here is to compute something that is
		monotonic in the probability the data is the result of a
		signal, and since this data cannot be from a signal the
		correct answer is -inf.

		.numerator/.denominator = +inf/+inf is mapped to ln Lambda
		= NaN.  This is sufficiently nonsensical that there is no
		correct interpretation.  A warning will be displayed when
		this is encountered.
		g        zinf/inf encountered)	numeratordenominatorr   isinfr   warningswarn)r   r   r   
lnP_signal	lnP_noiser   r   r   rA   g	  s    (
zLnLikelihoodRatioMixin.__call__Nc       
      c   sh   |dkr| j }| j}n|j }|j}x@|D ]8\}}}|||}|||}	| |||| |	| fV  q(W dS )a  
		Generator that transforms a sequence of candidate parameter
		samples into a sequence of log likelihood ratio samples.

		random_params_seq is a sequence (generator is OK) yielding
		3-element tuples whose first two elements provide the *args
		and **kwargs values to be passed to the .numerator and
		.denominator functions, and whose third element is the
		natural logarithm of the probability density from which the
		(*args, **kwargs) parameters have been drawn evaluated at
		those parameters.

		The output of this generator is a sequence of 3-element
		tuples, each of whose elements are:

		1.  a value of the natural logarithm of the likelihood
		ratio,

		2.  the natural logarithm of the relative frequency of
		occurance of that likelihood ratio in the signal population
		corrected for the relative frequency at which the
		random_params_seq sampler is causing that value to be
		returned, and

		3.  the natural logarithm of the relative frequency of
		occurance of that likelihood ratio in the noise population
		similarly corrected for the relative frequency at which the
		random_params_seq sampler is causing that value to be
		returned.

		The intention is for the first element of each tuple to be
		added to histograms using the two relative frequencies as
		weights, i.e., the two relative frequencies give the number
		of times one should consider this one draw of log
		likelihood ratio to have occured in the two populations.

		On each iteration, the *args and **kwargs values yielded by
		random_params_seq are passed to our own .__call__() method
		to evalute the log likelihood ratio at that choice of
		parameter values.  The parameters are also passed to the
		.__call__() mehods of our own .numerator and .denominator
		attributes to obtain the signal and noise population
		densities at those parameters.

		If signal_noise_pdfs is not None then, instead of using our
		own .numerator and .denominator attributes, the parameters
		are passed to the .__call__() methods of its .numerator and
		.denominator attributes to obtain those densities.  This
		allows the distribution of ranking statistic values
		obtained from alternative signal and noise populations to
		be modelled.  This is sometimes useful for diagnostic
		purposes.

		Normalizations:

		Within the context of the intended application, it is
		sufficient for all of the probabilities involved (the
		.numerator and .denominator probability densities, and the
		probability density supplied by the random_params_seq
		geneator) to be correct up to unknown normalization
		constants, i.e., the natural logarithms of the probabilties
		to be correct up to unknown additive constants.  That is
		why the two probability densities yielded by each iteration
		of this generator are described as relative frequencies:
		the ratios among their values are meaningful, but not their
		absolute values.

		If all of the supplied probabilities are, in fact, properly
		normalized, then the relative frequencies returned by this
		generator are, also, correctly normalized probability
		densities.
		N)r   r   )
r   Zrandom_params_seqZsignal_noise_pdfsZlnP_signal_funcZlnP_noise_funcr   r   Z
lnP_paramsr   r   r   r   r   ln_lr_samples	  s    I

z$LnLikelihoodRatioMixin.ln_lr_samples)N)r8   r9   r:   r;   rA   r   r   r   r   r   r   $	  s   BEr   )/r;   bisectr   Zfpconstr   ImportErrorr   rf   r   r|   r   Zscipy.optimizer   r   r   collectionsr   r   r   Zligo.lwr   r   Zligo.lw.utilsr	   r   Zligor
   r    r   
__author__Zgit_versionr   __date__r   __version__r   rY   r   r>   rZ   rt   r   r   r   r   r   r   r   r   r   <module>   s\      b    M V`   D  <t