a
    ߙfb9                     @   s  d Z ddlZddlZddlmZ ddlmZmZmZm	Z	m
Z
 ddlmZ ddlmZ ddlZddlZddgZed	d
ZG dd deZeej ejfZG dd dejZedddZedddZG dd deZeddZG dd deZG dd de Z!G dd deZ"dS )z4
This module is to contain an improved bounding box
    N)
namedtuple)DictListTupleCallableAny)
isiterableQuantityModelBoundingBoxCompoundBoundingBox_BaseIntervalzlower upperc                   @   s\   e Zd ZdZdd Zdd Zedd Zedd	 Z	ed
d Z
ejdddZdd ZdS )	_Intervala  
    A single input's bounding box interval.

    Parameters
    ----------
    lower : float
        The lower bound of the interval

    upper : float
        The upper bound of the interval

    Methods
    -------
    validate :
        Contructs a valid interval

    outside :
        Determine which parts of an input array are outside the interval.

    domain :
        Contructs a discretization of the points inside the interval.
    c                 C   s   d| j  d| j dS )NzInterval(lower=z, upper=))lowerupperself r   <lib/python3.9/site-packages/astropy/modeling/bounding_box.py__repr__3   s    z_Interval.__repr__c                 C   s
   t | S N)copydeepcopyr   r   r   r   r   6   s    z_Interval.copyc                 C   s   d}zt | }W n` tyr   z,t| dkr6| d } t dd | D }W n  tttfyl   t|Y n0 Y n0 |dv }|st|dko|d dkotdd	 | D }t| r|st|d
S )z0Validate the shape of an interval representationz5An interval must be some sort of sequence of length 2   r   c                 S   s   g | ]}|  qS r   )Zto_value.0br   r   r   
<listcomp>E       z-_Interval._validate_shape.<locals>.<listcomp>))   )r   r    )r    r   r    c                 s   s   | ]}t |tjV  qd S r   )
isinstancenpndarrayr   r   r   r   	<genexpr>L   r   z,_Interval._validate_shape.<locals>.<genexpr>N)r"   shape	TypeErrorlen
ValueErrorAttributeErrorallr   )intervalZMESSAGEr%   Zvalid_shaper   r   r   _validate_shape9   s"    z_Interval._validate_shapec                 C   s<   t |t |k r2td| d| dt | ||S )zGValidate the bounds are reasonable and construct an interval from them.zInvalid interval: upper bound z# is strictly less than lower bound .)r"   
asanyarrayr*   warningswarnRuntimeWarning)clsr   r   r   r   r   _validate_boundsQ   s    z_Interval._validate_boundsc                 C   s@   |  | t|dkr$t|d }nt|}| |d |d S )z
        Construct and validate an interval

        Parameters
        ----------
        interval : iterable
            A representation of the interval.

        Returns
        -------
        A validated interval.
        r   r   )r,   r'   tupler3   )r2   r+   r   r   r   validateZ   s
    
z_Interval.validate)_inputc                 C   s   t || jk || jkS )aU  
        Parameters
        ----------
        _input : np.ndarray
            The evaluation input in the form of an array.

        Returns
        -------
        Boolean array indicating which parts of _input are outside the interval:
            True  -> position outside interval
            False -> position inside  interval
        )r"   Z
logical_orr   r   )r   r6   r   r   r   outsideq   s    z_Interval.outsidec                 C   s   t | j| j| |S r   )r"   Zaranger   r   )r   
resolutionr   r   r   domain   s    z_Interval.domainN)__name__
__module____qualname____doc__r   r   staticmethodr,   classmethodr3   r5   r"   r#   r7   r9   r   r   r   r   r      s   


r   c                   @   s   e Zd ZdZdd Zdd ZejedddZ	eje
eeef d	d
dZedd Zdd Zdd Zdd Zdd ZeedddZeedddZeedddZedd Zed d!d"Zd#S )$_BoundingDomaina  
    Base class for ModelBoundingBox and CompoundBoundingBox.
        This is where all the `~astropy.modeling.core.Model` evaluation
        code for evaluating with a bounding box is because it is common
        to both types of bounding box.

    Parameters
    ----------
    model : `~astropy.modeling.Model`
        The Model this bounding domain is for.

    prepare_inputs :
        Generates the necessary input information so that model can
        be evaluated only for input points entirely inside bounding_box.
        This needs to be implemented by a subclass. Note that most of
        the implementation is in ModelBoundingBox.

    prepare_outputs :
        Fills the output values in for any input points outside the
        bounding_box.

    evaluate :
        Performs a complete model evaluation while enforcing the bounds
        on the inputs and returns a complete output.
    c                 C   s
   || _ d S r   )_modelr   modelr   r   r   __init__   s    z_BoundingDomain.__init__c                 O   s   t dd S )NzPThis bounding box is fixed by the model and does not have adjustable parameters.NotImplementedError)r   argskwargsr   r   r   __call__   s    z_BoundingDomain.__call__fixed_inputsc                 C   s   t ddS )C  
        Fix the bounding_box for a `fix_inputs` compound model.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The new model for which this will be a bounding_box
        fixed_inputs : dict
            Dictionary of inputs which have been fixed by this bounding box.
        z,This should be implemented by a child class.NrE   )r   rC   rK   r   r   r   
fix_inputs   s    z_BoundingDomain.fix_inputsreturnc                 C   s   t ddS )|  
        Get prepare the inputs with respect to the bounding box.

        Parameters
        ----------
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        inputs : list
            List of all the model inputs

        Returns
        -------
        valid_inputs : list
            The inputs reduced to just those inputs which are all inside
            their respective bounding box intervals
        valid_index : array_like
            array of all indices inside the bounding box
        all_out: bool
            if all of the inputs are outside the bounding_box
        z1This has not been implemented for BoundingDomain.NrE   )r   input_shapeinputsr   r   r   prepare_inputs   s    z_BoundingDomain.prepare_inputsc                 C   s   t | | S )a  
        Create a baseline output, assuming that the entire input is outside
        the bounding box

        Parameters
        ----------
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box

        Returns
        -------
        An array of the correct shape containing all fill_value
        )r"   zeros)rQ   
fill_valuer   r   r   _base_output   s    z_BoundingDomain._base_outputc                    s"    fddt jjD dfS )a  
        Create output if all inputs are outside the domain

        Parameters
        ----------
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box

        Returns
        -------
        A full set of outputs for case that all inputs are outside domain.
        c                    s   g | ]}  qS r   )rV   )r   _rU   rQ   r   r   r   r      s   z3_BoundingDomain._all_out_output.<locals>.<listcomp>N)rangerA   	n_outputs)r   rQ   rU   r   rX   r   _all_out_output   s
    
z_BoundingDomain._all_out_outputc                 C   s>   |  ||}|jst|}n|||< t|r:|d}|S )a
  
        For a single output fill in all the parts corresponding to inputs
        outside the bounding box.

        Parameters
        ----------
        valid_output : numpy array
            The output from the model corresponding to inputs inside the
            bounding box
        valid_index : numpy array
            array of all indices of inputs inside the bounding box
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box

        Returns
        -------
        An output array with all the indices corresponding to inputs
        outside the bounding box filled in by fill_value
        r   )rV   r%   r"   Zarrayisscalaritem)r   valid_outputvalid_indexrQ   rU   outputr   r   r   _modify_output   s    

z_BoundingDomain._modify_outputc              	   C   s(   g }|D ]}| | |||| q|S )a  
        Fill in all the outputs of the model corresponding to inputs
        outside the bounding_box.

        Parameters
        ----------
        valid_outputs : list of numpy array
            The list of outputs from the model corresponding to inputs
            inside the bounding box
        valid_index : numpy array
            array of all indices of inputs inside the bounding box
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box

        Returns
        -------
        List of filled in output arrays.
        )appendra   )r   valid_outputsr_   rQ   rU   outputsr^   r   r   r   _prepare_outputs  s    z _BoundingDomain._prepare_outputsc                 C   s"   | j jdkr|g}| ||||S )a  
        Fill in all the outputs of the model corresponding to inputs
        outside the bounding_box, adjusting any single output model so that
        its output becomes a list of containing that output.

        Parameters
        ----------
        valid_outputs : list
            The list of outputs from the model corresponding to inputs
            inside the bounding box
        valid_index : array_like
            array of all indices of inputs inside the bounding box
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box
        r   )rA   rZ   re   )r   rc   r_   rQ   rU   r   r   r   prepare_outputs8  s    z_BoundingDomain.prepare_outputs)
with_unitsc                 C   s   |rt | ddS dS )aD  
        Get the unit for outputs if one is required.

        Parameters
        ----------
        valid_outputs : list of numpy array
            The list of outputs from the model corresponding to inputs
            inside the bounding box
        with_units : bool
            whether or not a unit is required
        ZunitN)getattr)rc   rg   r   r   r   _get_valid_outputs_unitP  s    z'_BoundingDomain._get_valid_outputs_unit)evaluaterg   c           	      C   s(   ||}|  ||}| |||||fS )a  
        Evaluate the model using the given evaluate routine

        Parameters
        ----------
        evaluate : Callable
            callable which takes in the valid inputs to evaluate model
        valid_inputs : list of numpy arrays
            The inputs reduced to just those inputs which are all inside
            their respective bounding box intervals
        valid_index : numpy array
            array of all indices inside the bounding box
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box
        with_units : bool
            whether or not a unit is required

        Returns
        -------
        outputs :
            list containing filled in output values
        valid_outputs_unit :
            the unit that will be attached to the outputs
        )ri   rf   )	r   rj   valid_inputsr_   rQ   rU   rg   rc   valid_outputs_unitr   r   r   _evaluate_modela  s    z_BoundingDomain._evaluate_modelc           	      C   s:   |  ||\}}}|r"| ||S | ||||||S dS )a  
        Perform model evaluation steps:
            prepare_inputs -> evaluate -> prepare_outputs

        Parameters
        ----------
        evaluate : Callable
            callable which takes in the valid inputs to evaluate model
        valid_inputs : list of numpy arrays
            The inputs reduced to just those inputs which are all inside
            their respective bounding box intervals
        valid_index : numpy array
            array of all indices inside the bounding box
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box
        with_units : bool
            whether or not a unit is required

        Returns
        -------
        outputs :
            list containing filled in output values
        valid_outputs_unit :
            the unit that will be attached to the outputs
        N)rS   r[   rm   )	r   rj   rR   rQ   rU   rg   rk   r_   all_outr   r   r   	_evaluate  s    
z_BoundingDomain._evaluatec                 C   s   |durt | |ddS | S )a  
        Set the units on the outputs
            prepare_inputs -> evaluate -> prepare_outputs -> set output units

        Parameters
        ----------
        outputs :
            list containing filled in output values
        valid_outputs_unit :
            the unit that will be attached to the outputs

        Returns
        -------
        List containing filled in output values and units
        NFr   r	   )rd   rl   r   r   r   _set_outputs_unit  s    z!_BoundingDomain._set_outputs_unit)rj   c                 C   s6   | j |}| ||||| j j\}}t| ||S )a  
        Perform full model evaluation steps:
            prepare_inputs -> evaluate -> prepare_outputs -> set output units

        Parameters
        ----------
        evaluate : callable
            callable which takes in the valid inputs to evaluate model
        valid_inputs : list
            The inputs reduced to just those inputs which are all inside
            their respective bounding box intervals
        valid_index : array_like
            array of all indices inside the bounding box
        fill_value : float
            The value which will be assigned to inputs which are outside
            the bounding box
        )rA   rQ   ro   Zbbox_with_unitsr4   rq   )r   rj   rR   rU   rQ   rd   rl   r   r   r   rj     s
    
z_BoundingDomain.evaluateN)r:   r;   r<   r=   rD   rI   abcabstractmethoddictrM   r   r   rS   r>   rV   r[   ra   re   rf   boolri   r   rm   ro   rq   rj   r   r   r   r   r@      s0   
"#&
r@   indexc                 C   s
   | j | S z3Get the input name corresponding to the input indexrR   )rC   rw   r   r   r   get_name  s    rz   rN   c                 C   s   t |tr<|| jv r"| j|}qtd| d| j dndtt|tjrd|  krht	| jk rrn n|}qt
d| dt	| j dntd| d|S )	z
    Get the input index corresponding to the given key.
        Can pass in either:
            the string name of the input or
            the input index itself.
    'z' is not one of the inputs: r-   r   zInteger key: z must be non-negative and < zKey value: z must be string or integer.)r!   strrR   rw   r(   r"   Z
issubdtypetypeZintegerr'   
IndexError)rC   keyrw   r   r   r   	get_index  s    

r   c                       s   e Zd ZdZdNeeef ee ed fddZ	dOddZ
eeeef d	d
dZeed	ddZeee d	ddZedddZeeeef d	ddZeee d	ddZdd Zed	ddZeee dddZdd Zd d! Zd"d# Zd$d% ZdPeed&d'd(ZdQed)d*d+Zd,d- Zd.d/ Zd0d1 Zed2d3d4Z dRed)d5d6Z!eed	d7d8Z"dSed)d9d:Z#dTed)d;d<Z$e%dUeed=d>d?Z&dVedAdBdCZ'edDdE Z(dWed)dFdGZ)dHdI Z*dJdK Z+e,e-e-e-f d	dLdMZ.  Z/S )Xr   a  
    A model's bounding box

    Parameters
    ----------
    intervals : dict
        A dictionary containing all the intervals for each model input
            keys   -> input index
            values -> interval for that index

    model : `~astropy.modeling.Model`
        The Model this bounding_box is for.

    ignored : list
        A list containing all the inputs (index) which will not be
        checked for whether or not their elements are in/out of an interval.

    order : optional, str
        The ordering that is assumed for the tuple representation of this
        bounding_box. Options: 'C': C/Python order, e.g. z, y, x.
        (default), 'F': Fortran/mathematical notation order, e.g. x, y, z.
    NC)	intervalsignoredorderc                    sF   t  | || _| || _i | _|dkrB|i krB| j||d d S )Nr   r   )superrD   _order_validate_ignored_ignored
_intervals	_validate)r   r   rC   r   r   	__class__r   r   rD     s    zModelBoundingBox.__init__c                 C   s:   dd | j  D }|d u r&| j }t|| j|| jdS )Nc                 S   s   i | ]\}}||  qS r   rp   )r   rw   r+   r   r   r   
<dictcomp>  s   z)ModelBoundingBox.copy.<locals>.<dictcomp>r   r   )r   itemsr   r   r   rA   r   )r   r   r   r   r   r   r     s    
zModelBoundingBox.copyrN   c                 C   s   | j S )z1Return bounding_box labeled using input positions)r   r   r   r   r   r   '  s    zModelBoundingBox.intervalsc                 C   s   | j S r   r   r   r   r   r   r   ,  s    zModelBoundingBox.orderc                 C   s   | j S r   r   r   r   r   r   r   0  s    zModelBoundingBox.ignoredrv   c                 C   s   t | j|S rx   )rz   rA   )r   rw   r   r   r   	_get_name4  s    zModelBoundingBox._get_namec                    s    fdd j  D S )z-Return bounding_box labeled using input namesc                    s   i | ]\}}  ||qS r   r   )r   rw   bboxr   r   r   r   ;  r   z4ModelBoundingBox.named_intervals.<locals>.<dictcomp>)r   r   r   r   r   r   named_intervals8  s    z ModelBoundingBox.named_intervalsc                    s    fdd j D S )Nc                    s   g | ]}  |qS r   r   )r   rw   r   r   r   r   ?  r   z3ModelBoundingBox.ignored_inputs.<locals>.<listcomp>r   r   r   r   r   ignored_inputs=  s    zModelBoundingBox.ignored_inputsc                 C   s   ddg}| j  D ]\}}|d| d|  q|d t| jdkr\|d| j  |d| jjj d	| jj	 d
 |d| j
 d |d
 d|S )NzModelBoundingBox(z    intervals={        z:     }r   z    ignored=z
    model=z(inputs=r   z    order='r{   
)r   r   rb   r'   r   r   rA   r   r:   rR   r   join)r   partsnamer+   r   r   r   r   A  s    
"
zModelBoundingBox.__repr__c                 C   s   t | j|S )z
        Get the input index corresponding to the given key.
            Can pass in either:
                the string name of the input or
                the input index itself.
        )r   rA   r   r   r   r   r   
_get_indexT  s    zModelBoundingBox._get_index)r   rO   c                    s"   |d u rg S  fdd|D S d S )Nc                    s   g | ]}  |qS r   )r   )r   r   r   r   r   r   b  r   z6ModelBoundingBox._validate_ignored.<locals>.<listcomp>r   )r   r   r   r   r   r   ^  s    z"ModelBoundingBox._validate_ignoredc                 C   s
   t | jS r   )r'   r   r   r   r   r   __len__d  s    zModelBoundingBox.__len__c              	   C   s6   z|  || jv p| jW S  ttfy0   Y dS 0 d S NF)r   r   r   r~   r(   r   r   r   r   __contains__g  s    zModelBoundingBox.__contains__c                 C   s   |  || jv S r   )r   r   r   r   r   r   has_intervalm  s    zModelBoundingBox.has_intervalc                 C   s,   |  |}|| jv rtS | j|  | S dS )z<Get bounding_box entries by either input name or input indexN)r   r   _ignored_intervalr   r   r   rw   r   r   r   __getitem__p  s    

zModelBoundingBox.__getitem__)r   rO   c                 C   s*   |du r| j }|dvr&td| d|S )zc
        Get if bounding_box is C/python ordered or Fortran/mathematically
        ordered
        N)r   FzTorder must be either 'C' (C/python order) or 'F' (Fortran/mathematical order), got: r-   )r   r(   )r   r   r   r   r   
_get_orderx  s    
zModelBoundingBox._get_orderr   c                    s~   t  jdkr$tt j d S  |} jj}|dkrL|ddd }t fdd|D }t |dkrv|d }|S dS )z
        Return the old tuple of tuples representation of the bounding_box
            order='C' corresponds to the old bounding_box ordering
            order='F' corresponds to the gwcs bounding_box ordering.
        r   r   r   Nc                    s   g | ]}t  | qS r   r4   r   Z
input_namer   r   r   r     r   z1ModelBoundingBox.bounding_box.<locals>.<listcomp>)r'   r   r4   listvaluesr   rA   rR   )r   r   rR   r   r   r   r   bounding_box  s    
zModelBoundingBox.bounding_boxc                 C   s@   t |tr|  |kS t |tr8| j|jko6| j|jkS dS dS )z?Note equality can be either with old representation or new one.FN)r!   r4   r   r   r   r   r   valuer   r   r   __eq__  s
    

zModelBoundingBox.__eq__c                 C   s4   |  |}|| jv r | j| t|| j|< dS )zBValidate and store interval under key (input index or input name).N)r   r   remover   r5   r   )r   r   r   rw   r   r   r   __setitem__  s    

zModelBoundingBox.__setitem__c                 C   s<   |  |}|| jv r$td| d| j|= | j| dS )zDelete stored intervalzCannot delete ignored input: !N)r   r   RuntimeErrorr   rb   r   r   r   r   __delitem__  s
    

zModelBoundingBox.__delitem__)r   c                 C   s   |  D ]\}}|| |< qdS )z:Validate passing dictionary of intervals and setting them.Nr   )r   r   r   r   r   r   r   _validate_dict  s    zModelBoundingBox._validate_dictc                 C   s>   |  |}|dkr |ddd }t|D ]\}}|| |< q(dS )zNValidate passing tuple of tuples representation (or related) and setting them.r   Nr   )r   	enumerate)r   r   r   rw   r   r   r   r   _validate_sequence  s
    
z#ModelBoundingBox._validate_sequencec                 C   s&   | j jt| j }|dkr|S dS d S )Nr   )rA   n_inputsr'   r   )r   r   r   r   r   	_n_inputs  s    zModelBoundingBox._n_inputsc                 C   sP   t || jkr*tdt | d| j dt|tr@| | n| || dS )z,Validate and set any iterable representationzFound z" intervals, but must have exactly r-   N)r'   r   r(   r!   rt   r   r   r   r   r   r   r   r   _validate_iterable  s    

z#ModelBoundingBox._validate_iterablec                 C   s.   | j dkrt|ts|| d< n| || dS )z#Validate and set any representationr   r   N)r   r!   rt   r   r   r   r   r   r     s    
zModelBoundingBox._validater   c                 K   s4   t |tr|j}|j}| i |||d}|| |S )a  
        Construct a valid bounding box for a model.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The model for which this will be a bounding_box
        bounding_box : dict, tuple
            A possible representation of the bounding box
        order : optional, str
            The order that a tuple representation will be assumed to be
                Default: 'C'
        r   )r!   r   r   r   r   )r2   rC   r   r   r   rH   newr   r   r   r5     s    

zModelBoundingBox.validateFrJ   c                 C   sB   |   }| D ]
}||= q|r(|j}nd}tj||j||jdS )a  
        Fix the bounding_box for a `fix_inputs` compound model.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The new model for which this will be a bounding_box
        fixed_inputs : dict
            Dictionary of inputs which have been fixed by this bounding box.
        keep_ignored : bool
            Keep the ignored inputs of the bounding box (internal argument only)
        Nr   )r   keysr   r   r5   r   r   )r   rC   rK   _keep_ignoredr   r6   r   r   r   r   rM     s    
zModelBoundingBox.fix_inputsc                 C   s   t | S r   )r'   r   r   r   r   	dimension  s    zModelBoundingBox.dimensionc                    s<   j j}|}|dkr(|d d d } fdd|D S )Nr   r   c                    s   g | ]}|   qS r   )r9   r   r8   r   r   r   r     r   z+ModelBoundingBox.domain.<locals>.<listcomp>)rA   rR   r   )r   r8   r   rR   r   r   r   r9     s
    
zModelBoundingBox.domainc                 C   sd   d}t j|td}t|D ]@\}}t |}t | | ||}d||< | rd} q\q||fS )a  
        Get all the input positions which are outside the bounding_box,
        so that the corresponding outputs can be filled with the fill
        value (default NaN).

        Parameters
        ----------
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        inputs : list
            List of all the model inputs

        Returns
        -------
        outside_index : bool-numpy array
            True  -> position outside bounding_box
            False -> position inside  bounding_box
        all_out : bool
            if all of the inputs are outside the bounding_box
        F)ZdtypeT)r"   rT   ru   r   r.   broadcast_tor7   r*   )r   rQ   rR   rn   outside_indexrw   r6   r7   r   r   r   _outside  s    
zModelBoundingBox._outsidec                 C   s@   |  ||\}}tt| }t|d dkr8d}||fS )a  
        Get the indices of all the inputs inside the bounding_box.

        Parameters
        ----------
        input_shape : tuple
            The shape that all inputs have be reshaped/broadcasted into
        inputs : list
            List of all the model inputs

        Returns
        -------
        valid_index : numpy array
            array of all indices inside the bounding box
        all_out : bool
            if all of the inputs are outside the bounding_box
        r   T)r   r"   
atleast_1dZlogical_notZnonzeror'   )r   rQ   rR   r   rn   r_   r   r   r   _valid_indexA  s
    zModelBoundingBox._valid_indexc                 C   st   |  ||\}}g }|sf|D ]H}|rZtt||| }t|rN|d}|| q|| qt|||fS )rP   r   )r   r"   r   r   r\   r]   rb   r4   )r   rQ   rR   r_   rn   rk   r6   Zvalid_inputr   r   r   rS   [  s    

zModelBoundingBox.prepare_inputs)Nr   )N)N)N)N)N)N)Nr   )F)N)0r:   r;   r<   r=   r   intr   r   r|   rD   r   propertyr   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   r   rt   r   r   r   r   r   r?   r5   rM   r   r9   r   r   r   r   rS   __classcell__r   r   r   r   r     s^    

	 
$_BaseSelectorArgumentzindex ignorec                       sv   e Zd ZdZ fddZededddZdd	 Ze	d
ddZ
dd ZedddZed
ddZdd Z  ZS )_SelectorArgumenta#  
    Contains a single CompoundBoundingBox slicing input.

    Parameters
    ----------
    index : int
        The index of the input in the input list

    ignore : bool
        Whether or not this input will be ignored by the bounding box.

    Methods
    -------
    validate :
        Returns a valid SelectorArgument for a given model.

    get_selector :
        Returns the value of the input for use in finding the correct
        bounding_box.

    get_fixed_value :
        Gets the slicing value from a fix_inputs set of values.
    c                    s   t  | ||}|S r   )r   __new__)r2   rw   ignorer   r   r   r   r     s    z_SelectorArgument.__new__T)r   c                 C   s   | t |||S )a  
        Construct a valid selector argument for a CompoundBoundingBox.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The model for which this will be an argument for.
        argument : int or str
            A representation of which evaluation input to use
        ignored : optional, bool
            Whether or not to ignore this argument in the ModelBoundingBox.

        Returns
        -------
        Validated selector_argument
        )r   )r2   rC   argumentr   r   r   r   r5     s    z_SelectorArgument.validatec                 G   s2   || j  }t|r.t|dkr&|d S t|S |S )z
        Get the selector value corresponding to this argument

        Parameters
        ----------
        *inputs :
            All the processed model evaluation inputs.
        r   r   )rw   r   r'   r4   r   rR   	_selectorr   r   r   get_selector  s    	
z_SelectorArgument.get_selectorrN   c                 C   s   t || jS )z
        Get the name of the input described by this selector argument

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model this selector argument is for.
        )rz   rw   rB   r   r   r   r     s    	z_SelectorArgument.namec                 C   s   d|  | d| j dS )z
        Get a pretty-print representation of this object

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model this selector argument is for.
        zArgument(name='z
', ignore=r   r   r   rB   r   r   r   pretty_repr  s    	z_SelectorArgument.pretty_reprr   c                 C   sL   | j |v r|| j  S | ||v r0|| | S t| | d| dS )a  
        Gets the value fixed input corresponding to this argument

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model this selector argument is for.

        values : dict
            Dictionary of fixed inputs.
        z was not found in N)rw   r   r   r   r   rC   r   r   r   r   get_fixed_value  s
    

z!_SelectorArgument.get_fixed_valuec                 C   s   | j t||kS )a?  
        Determine if passed argument is described by this selector argument

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model this selector argument is for.

        argument : int or str
            A representation of which evaluation input is being used
        )rw   r   )r   rC   r   r   r   r   is_argument  s    z_SelectorArgument.is_argumentc                 C   s   |  || jfS )z
        Get a tuple representation of this argument using the input
        name from the model.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model this selector argument is for.
        r   rB   r   r   r   named_tuple  s    
z_SelectorArgument.named_tuple)T)r:   r;   r<   r=   r   r?   ru   r5   r   r|   r   r   rt   r   r   r   r   r   r   r   r   r     s   r   c                       s   e Zd ZdZdZd!ee ed fddZdd Z	e
dd	 Ze
d
d Zed"edddZdd Zdd ZedddZedddZdd Zdd Zdd Zdd  Z  ZS )#_SelectorArgumentsa  
    Contains the CompoundBoundingBox slicing description

    Parameters
    ----------
    input_ :
        The SelectorArgument values

    Methods
    -------
    validate :
        Returns a valid SelectorArguments for its model.

    get_selector :
        Returns the selector a set of inputs corresponds to.

    is_selector :
        Determines if a selector is correctly formatted for this CompoundBoundingBox.

    get_fixed_value :
        Gets the selector from a fix_inputs set of values.
    N)input_kept_ignorec                    s(   t  | |}|d u rg |_n||_|S r   )r   r   _kept_ignore)r2   r   r   r   r   r   r   r   &  s
    z_SelectorArguments.__new__c                 C   s:   dg}| D ]}| d||  q
| d d|S )z
        Get a pretty-print representation of this object

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.
        zSelectorArguments(z    r   r   )rb   r   r   )r   rC   r   r   r   r   r   r   0  s    	
z_SelectorArguments.pretty_reprc                 C   s   dd | D }| | j |S )zGet the list of ignored inputsc                 S   s   g | ]}|j r|jqS r   )r   rw   r   r   r   r   r   r   E  r   z-_SelectorArguments.ignore.<locals>.<listcomp>)extendr   )r   r   r   r   r   r   B  s    z_SelectorArguments.ignorec                 C   s   | j S )z$The arguments to persist in ignoring)r   r   r   r   r   r   J  s    z_SelectorArguments.kept_ignore)r   c                 C   s   g }|D ]L}t j|g|R  }|jdd |D v rJtdt||j d|| qt|dkrjtdt|tr|du rg }|	|j
 | t||S )ae  
        Construct a valid Selector description for a CompoundBoundingBox.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.

        arguments :
            The individual argument informations

        kept_ignore :
            Arguments to persist as ignored
        c                 S   s   g | ]
}|j qS r   rv   )r   thisr   r   r   r   b  r   z/_SelectorArguments.validate.<locals>.<listcomp>zInput: 'z' has been repeated.r   z-There must be at least one selector argument.N)r   r5   rw   r(   rz   rb   r'   r!   r   r   r   r4   )r2   rC   	argumentsr   rR   r   r6   r   r   r   r5   O  s    
z_SelectorArguments.validatec                    s   t  fdd| D S )z
        Get the selector corresponding to these inputs

        Parameters
        ----------
        *inputs :
            All the processed model evaluation inputs.
        c                    s   g | ]}|j   qS r   )r   r   ry   r   r   r   z  r   z3_SelectorArguments.get_selector.<locals>.<listcomp>r   )r   rR   r   ry   r   r   q  s    	z_SelectorArguments.get_selectorc                 C   s   t |tot|t| kS )z
        Determine if this is a reasonable selector

        Parameters
        ----------
        _selector : tuple
            The selector to check
        )r!   r4   r'   r   r   r   r   r   is_selector|  s    	z_SelectorArguments.is_selectorr   c                    s   t  fdd| D S )a  
        Gets the value fixed input corresponding to this argument

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.

        values : dict
            Dictionary of fixed inputs.
        c                    s   g | ]}|  qS r   )r   r   rC   r   r   r   r     r   z7_SelectorArguments.get_fixed_values.<locals>.<listcomp>r   r   r   r   r   get_fixed_values  s    z#_SelectorArguments.get_fixed_valuesrN   c                 C   s    | D ]}| ||r dS qdS )a<  
        Determine if passed argument is one of the selector arguments

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.

        argument : int or str
            A representation of which evaluation input is being used
        TFN)r   )r   rC   r   selector_argr   r   r   r     s    z_SelectorArguments.is_argumentc                 C   s8   t | D ]\}}|||r|  S qt| ddS )a2  
        Get the index of the argument passed in the selector tuples

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.

        argument : int or str
            A representation of which argument is being used
        z. does not correspond to any selector argument.N)r   r   r(   )r   rC   r   rw   r   r   r   r   selector_index  s    
z!_SelectorArguments.selector_indexc                 C   s<   t | }|| ||jg}|| j t|t||S )a*  
        Reduce the selector arguments by the argument given

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.

        argument : int or str
            A representation of which argument is being used
        )	r   popr   rw   r   r   r   r5   r4   )r   rC   r   r   r   r   r   r   reduce  s    z_SelectorArguments.reducec                 C   s4   |  ||rt| dt||g}t|| |S )a  
        Add argument to the kept_ignore list

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.

        argument : int or str
            A representation of which argument is being used
        z/: is a selector argument and cannot be ignored.)r   r(   r   r   r5   )r   rC   r   r   r   r   r   
add_ignore  s    z_SelectorArguments.add_ignorec                    s   t  fdd| D S )z
        Get a tuple of selector argument tuples using input names

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The Model these selector arguments are for.
        c                    s   g | ]}|  qS r   )r   )r   r   rC   r   r   r     r   z2_SelectorArguments.named_tuple.<locals>.<listcomp>r   rB   r   r   r   r     s    	z_SelectorArguments.named_tuple)N)N)r:   r;   r<   r=   r   r   r   r   r   r   r   r   r   r?   r5   r   r   rt   r   ru   r   r   r   r   r   r   r   r   r   r   r     s$   


!r   c                       sb  e Zd ZdZd6eeef eee	d fddZ
dd Zd	d
 Zeeeef dddZeedddZejdd ZeedddZedd Zee	dddZedd Zdd ZedddZdd Zed7ee	d d!d"Zd#d$ Zd%d& Zd'd( Zedd)d*Z e!eeef dd+d,Z"eeef dd-d.Z#d/d0 Z$d1d2 Z%ed3d4d5Z&  Z'S )8r   a  
    A model's compound bounding box

    Parameters
    ----------
    bounding_boxes : dict
        A dictionary containing all the ModelBoundingBoxes that are possible
            keys   -> _selector (extracted from model inputs)
            values -> ModelBoundingBox

    model : `~astropy.modeling.Model`
        The Model this compound bounding_box is for.

    selector_args : _SelectorArguments
        A description of how to extract the selectors from model inputs.

    create_selector : optional
        A method which takes in the selector and the model to return a
        valid bounding corresponding to that selector. This can be used
        to construct new bounding_boxes for previously undefined selectors.
        These new boxes are then stored for future lookups.

    order : optional, str
        The ordering that is assumed for the tuple representation of the
        bounding_boxes.
    Nr   )bounding_boxesselector_argscreate_selectorr   c                    s:   t  | || _|| _t||| _i | _| | d S r   )	r   rD   r   _create_selectorr   r5   _selector_args_bounding_boxesr   )r   r   rC   r   r   r   r   r   r   rD   	  s    zCompoundBoundingBox.__init__c                    s8    fdd j  D }t| j jt j jdS )Nc                    s    i | ]\}}||  jjqS r   )r   r   r   )r   Zselectorr   r   r   r   r     s   z,CompoundBoundingBox.copy.<locals>.<dictcomp>)r   r   r   )	r   r   r   rA   r   r   r   r   r   )r   r   r   r   r   r     s    

zCompoundBoundingBox.copyc              	   C   s   ddg}| j  D ]L\}}| d}|d| d|d  |D ]}|d|  qHq|d | j| jd}|d	|d  |D ]}|d|  q|d
 d	|S )NzCompoundBoundingBox(z    bounding_boxes={r   r   z = r   z            r   z    selector_args = r   )
r   r   r   splitrb   r   r   r   rA   r   )r   r   r   r   Z	bbox_reprpartZselector_args_reprr   r   r   r     s    

zCompoundBoundingBox.__repr__rN   c                 C   s   | j S r   r   r   r   r   r   r   1  s    z"CompoundBoundingBox.bounding_boxesc                 C   s   | j S r   )r   r   r   r   r   r   5  s    z!CompoundBoundingBox.selector_argsc                 C   s    t | j|| _tdt d S )NzhOverriding selector_args may cause problems you should re-validate the compound bounding box before use!)r   r5   rA   r   r/   r0   r1   r   r   r   r   r   9  s    c                 C   s   | j | jS r   )r   r   rA   r   r   r   r   named_selector_tuple@  s    z(CompoundBoundingBox.named_selector_tuplec                 C   s   | j S r   )r   r   r   r   r   r   D  s    z#CompoundBoundingBox.create_selectorc                 C   s   | j S r   r   r   r   r   r   r   H  s    zCompoundBoundingBox.orderc                 C   s   t | rt| S | fS d S r   )r   r4   )r   r   r   r   _get_selector_keyL  s    z%CompoundBoundingBox._get_selector_keyc                 C   sH   |  |}| j|s$t| dtj| j|| jj| jd| j	|< d S )Nz is not a selector!r   )
r   r   r   r(   r   r5   rA   r   r   r   )r   r   r   r   r   r   r   r   S  s    

zCompoundBoundingBox.__setitem__)r   c                 C   s   |  D ]\}}|| |< qd S r   r   )r   r   r   r   r   r   r   r   \  s    zCompoundBoundingBox._validatec                 C   s6   t |tr.| j|jko,| j|jko,| j|jkS dS d S r   )r!   r   r   r   r   r   r   r   r   r   `  s    


zCompoundBoundingBox.__eq__)r   r   c                 C   sR   t |tr2|du r|j}|du r&|j}|j}|j}|du rBtd| |||||S )ac  
        Construct a valid compound bounding box for a model.

        Parameters
        ----------
        model : `~astropy.modeling.Model`
            The model for which this will be a bounding_box
        bounding_box : dict
            Dictionary of possible bounding_box respresentations
        selector_args : optional
            Description of the selector arguments
        create_selector : optional, callable
            Method for generating new selectors
        order : optional, str
            The order that a tuple representation will be assumed to be
                Default: 'C'
        NzUSelector arguments must be provided (can be passed as part of bounding_box argument)!)r!   r   r   r   r   r   r(   )r2   rC   r   r   r   r   r   r   r   r5   h  s    
zCompoundBoundingBox.validatec                 C   s
   || j v S r   r   r   r   r   r   r     s    z CompoundBoundingBox.__contains__c                 C   s   | j || jd| |< | | S )Nr   )r   rA   r   r   r   r   _create_bounding_box  s    z(CompoundBoundingBox._create_bounding_boxc                 C   sD   |  |}|| v r| j| S | jd ur0| |S td| dd S )Nz)No bounding box is defined for selector: r-   )r   r   r   r   r   )r   r   r   r   r   r   r     s    



zCompoundBoundingBox.__getitem__c                 C   s   | j j| }| | S r   )r   r   r   r   r   r   _select_bounding_box  s    z(CompoundBoundingBox._select_bounding_boxc                 C   s   |  |}|||S )rP   )r   rS   )r   rQ   rR   r   r   r   r   rS     s    
z"CompoundBoundingBox.prepare_inputsc           	      C   s   | j | j|}i }| j D ]\\}}|| |krt|}|| ||rf|j| j||idd}n|	 }||t
|< qt|dkrtd| d| d|S )NTr   r   zAttempting to fix input z5, but there are no bounding boxes for argument value r-   )r   r   rA   r   r   r   r   r   rM   r   r4   r'   r(   )	r   r   r   r   Zmatchingselector_keyr   Znew_selector_keyZnew_bboxr   r   r   _matching_bounding_boxes  s"    



z,CompoundBoundingBox._matching_bounding_boxesc                 C   s@   |  ||}t| jdkr"|d S t|| j| j| j|S d S )Nr   r   )r   r'   r   r   rA   r   )r   r   r   Zmatching_bounding_boxesr   r   r   _fix_input_selector_arg  s    z+CompoundBoundingBox._fix_input_selector_argc                 C   sL   i }| j  D ]"\}}|j| j||idd||< qt|| j| j| j|S )NTr   )r   r   rM   rA   r   r   r   )r   r   r   r   r   r   r   r   r   _fix_input_bbox_arg  s    z'CompoundBoundingBox._fix_input_bbox_argrJ   c           
      C   s   t | }| }|| }| j| j|r:| ||}n| ||}t|dkrl|	 }||= |
||}t|tr|j}|}	nt|trd}|j}	|jj||	|j|dS )rL   r   N)r   r   )r   r   r   r   r   rA   r   r   r'   r   rM   r!   r   r   r   r   r   r5   r   )
r   rC   rK   Zfixed_input_keysr   r   r   Znew_fixed_inputsr   Z	bbox_dictr   r   r   rM     s&    


zCompoundBoundingBox.fix_inputs)Nr   )NNr   )(r:   r;   r<   r=   r   r   r   r   r   r|   rD   r   r   r   r   r   setterr4   r   r   r   r>   r   r   rt   r   r   r?   r5   r   r   r   r   r   rS   r   r   r   rM   r   r   r   r   r   r     sN    	


	   			)#r=   rr   r   collectionsr   typingr   r   r   r   r   Zastropy.utilsr   Zastropy.unitsr
   r/   Znumpyr"   __all__r   r   r5   infr   ABCr@   r   rz   r   r   r   r   r4   r   r   r   r   r   r   <module>   s6   
j  W   

 
 c