a
    ߙfbS*                     @   s\   d Z ddlmZmZ ddlmZ g dZG dd deZG dd	 d	eZG d
d deZ	dS )z
Special models useful for complex compound models where control is needed over
which outputs from a source model are mapped to which inputs of a target model.
   )FittableModelModel    )Quantity)MappingIdentityUnitsMappingc                       sf   e Zd ZdZdZd fdd	Zedd Zedd	 Zed
d Z	dd Z
dd Zedd Z  ZS )r   a  
    Allows inputs to be reordered, duplicated or dropped.

    Parameters
    ----------
    mapping : tuple
        A tuple of integers representing indices of the inputs to this model
        to return and in what order to return them.  See
        :ref:`astropy:compound-model-mappings` for more details.
    n_inputs : int
        Number of inputs; if `None` (default) then ``max(mapping) + 1`` is
        used (i.e. the highest input index used in the mapping).
    name : str, optional
        A human-friendly name associated with this model instance
        (particularly useful for identifying the individual components of a
        compound model).
    meta : dict-like
        Free-form metadata to associate with this model.

    Raises
    ------
    TypeError
        Raised when number of inputs is less that ``max(mapping)``.

    Examples
    --------

    >>> from astropy.modeling.models import Polynomial2D, Shift, Mapping
    >>> poly1 = Polynomial2D(1, c0_0=1, c1_0=2, c0_1=3)
    >>> poly2 = Polynomial2D(1, c0_0=1, c1_0=2.4, c0_1=2.1)
    >>> model = (Shift(1) & Shift(2)) | Mapping((0, 1, 0, 1)) | (poly1 & poly2)
    >>> model(1, 2)  # doctest: +FLOAT_CMP
    (17.0, 14.2)
    TNc                    s   d| _ d| _|d u r$t|d | _n|| _t|| _t j||d tdd t	| jD | _
tdd t	| jD | _|| _dd | j D | _d	d | j D | _d S )
N r   namemetac                 s   s   | ]}d t | V  qdS xNstr.0idxr	   r	   8lib/python3.9/site-packages/astropy/modeling/mappings.py	<genexpr>>       z#Mapping.__init__.<locals>.<genexpr>c                 s   s   | ]}d t | V  qdS r   r   r   r	   r	   r   r   ?   r   c                 S   s   i | ]
}|d qS Fr	   r   keyr	   r	   r   
<dictcomp>B   r   z$Mapping.__init__.<locals>.<dictcomp>c                 S   s   i | ]
}|d qS r   r	   r   r	   r	   r   r   C   r   )_inputs_outputsmax	_n_inputslen
_n_outputssuper__init__tuplerangeinputsoutputs_mapping_input_units_strict _input_units_allow_dimensionless)selfmappingn_inputsr   r   	__class__r	   r   r"   3   s    
zMapping.__init__c                 C   s   | j S N)r   r*   r	   r	   r   r,   E   s    zMapping.n_inputsc                 C   s   | j S r/   )r    r0   r	   r	   r   	n_outputsI   s    zMapping.n_outputsc                 C   s   | j S )z,Integers representing indices of the inputs.r'   r0   r	   r	   r   r+   M   s    zMapping.mappingc                 C   s.   | j d u rd| j dS d| j d| j dS )Nz	<Mapping()>, name=r   r+   r0   r	   r	   r   __repr__R   s    
zMapping.__repr__c                    sn   t  | jkr@| jd ur| jnd}t| d| j dt   t fdd| jD }| jdkrj|d S |S )Nr   z	 expects z inputs; got c                 3   s   | ]} | V  qd S r/   r	   r   argsr	   r   r   ]   r   z#Mapping.evaluate.<locals>.<genexpr>r   r   )r   r,   r   	TypeErrorr#   r'   r1   )r*   r8   r   resultr	   r7   r   evaluateW   s    
zMapping.evaluatec                    sz   z t  fddt jD }W n" tyB   td jY n0  |} j|_	 j	|_t
|j	|_t
|j|_|S )a)  
        A `Mapping` representing the inverse of the current mapping.

        Raises
        ------
        `NotImplementedError`
            An inverse does no exist on mappings that drop some of its inputs
            (there is then no way to reconstruct the inputs that were dropped).
        c                 3   s   | ]} j |V  qd S r/   )r+   indexr   r0   r	   r   r   q   s   z"Mapping.inverse.<locals>.<genexpr>zZMappings such as {} that drop one or more of their inputs are not invertible at this time.)r#   r$   r,   
ValueErrorNotImplementedErrorformatr+   r.   r   r   r   r   r    )r*   r+   invr	   r0   r   inversed   s     

zMapping.inverse)NNN)__name__
__module____qualname____doc__linearr"   propertyr,   r1   r+   r6   r;   rA   __classcell__r	   r	   r-   r   r      s   "


r   c                       s:   e Zd ZdZdZd
 fdd	Zdd Zedd	 Z  Z	S )r   a  
    Returns inputs unchanged.

    This class is useful in compound models when some of the inputs must be
    passed unchanged to the next model.

    Parameters
    ----------
    n_inputs : int
        Specifies the number of inputs this identity model accepts.
    name : str, optional
        A human-friendly name associated with this model instance
        (particularly useful for identifying the individual components of a
        compound model).
    meta : dict-like
        Free-form metadata to associate with this model.

    Examples
    --------

    Transform ``(x, y)`` by a shift in x, followed by scaling the two inputs::

        >>> from astropy.modeling.models import (Polynomial1D, Shift, Scale,
        ...                                      Identity)
        >>> model = (Shift(1) & Identity(1)) | Scale(1.2) & Scale(2)
        >>> model(1,1)  # doctest: +FLOAT_CMP
        (2.4, 2.0)
        >>> model.inverse(2.4, 2) # doctest: +FLOAT_CMP
        (1.0, 1.0)
    TNc                    s"   t t|}t j|||d d S )Nr
   )r#   r$   r!   r"   )r*   r,   r   r   r+   r-   r	   r   r"      s    zIdentity.__init__c                 C   s.   | j d u rd| j dS d| j d| j dS )Nz
<Identity(r3   r4   )r   r,   r0   r	   r	   r   r6      s    
zIdentity.__repr__c                 C   s   | S )zl
        The inverse transformation.

        In this case of `Identity`, ``self.inverse is self``.
        r	   r0   r	   r	   r   rA      s    zIdentity.inverse)NN)
rB   rC   rD   rE   rF   r"   r6   rG   rA   rH   r	   r	   r-   r   r      s   r   c                       s   e Zd ZdZd fdd	Zdd Zedd	 Zed
d Ze fddZ	e	j
 fddZ	e fddZej
 fddZedd Zedd Zdd Zdd Z  ZS )r   a
  
    Mapper that operates on the units of the input, first converting to
    canonical units, then assigning new units without further conversion.
    Used by Model.coerce_units to support units on otherwise unitless models
    such as Polynomial1D.

    Parameters
    ----------
    mapping : tuple
        A tuple of (input_unit, output_unit) pairs, one per input, matched to the
        inputs by position.  The first element of the each pair is the unit that
        the model will accept (specify ``dimensionless_unscaled``
        to accept dimensionless input).  The second element is the unit that the
        model will return.  Specify ``dimensionless_unscaled``
        to return dimensionless Quantity, and `None` to return raw values without
        Quantity.
    input_units_equivalencies : dict, optional
        Default equivalencies to apply to input values.  If set, this should be a
        dictionary where each key is a string that corresponds to one of the
        model inputs.
    input_units_allow_dimensionless : dict or bool, optional
        Allow dimensionless input. If this is True, input values to evaluate will
        gain the units specified in input_units. If this is a dictionary then it
        should map input name to a bool to allow dimensionless numbers for that
        input.
    name : str, optional
        A human-friendly name associated with this model instance
        (particularly useful for identifying the individual components of a
        compound model).
    meta : dict-like, optional
        Free-form metadata to associate with this model.

    Examples
    --------

    Wrapping a unitless model to require and convert units:

    >>> from astropy.modeling.models import Polynomial1D, UnitsMapping
    >>> from astropy import units as u
    >>> poly = Polynomial1D(1, c0=1, c1=2)
    >>> model = UnitsMapping(((u.m, None),)) | poly
    >>> model = model | UnitsMapping(((None, u.s),))
    >>> model(u.Quantity(10, u.m))  # doctest: +FLOAT_CMP
    <Quantity 21. s>
    >>> model(u.Quantity(1000, u.cm)) # doctest: +FLOAT_CMP
    <Quantity 21. s>
    >>> model(u.Quantity(10, u.cm)) # doctest: +FLOAT_CMP
    <Quantity 1.2 s>

    Wrapping a unitless model but still permitting unitless input:

    >>> from astropy.modeling.models import Polynomial1D, UnitsMapping
    >>> from astropy import units as u
    >>> poly = Polynomial1D(1, c0=1, c1=2)
    >>> model = UnitsMapping(((u.m, None),), input_units_allow_dimensionless=True) | poly
    >>> model = model | UnitsMapping(((None, u.s),))
    >>> model(u.Quantity(10, u.m))  # doctest: +FLOAT_CMP
    <Quantity 21. s>
    >>> model(10)  # doctest: +FLOAT_CMP
    <Quantity 21. s>
    NFc                    sb   || _ tdd |D }|dkr4|t|kr4tdd| _|| _|| _t j||d |   d S )Nc                 S   s   g | ]}|d  du r|qS )Nr	   )r   mr	   r	   r   
<listcomp>   r   z)UnitsMapping.__init__.<locals>.<listcomp>r   z1If one return unit is None, then all must be NoneTr
   )	r'   r   r=   r(   input_units_equivalenciesr)   r!   r"   _rebuild_units)r*   r+   rL   Zinput_units_allow_dimensionlessr   r   Znone_mapping_countr-   r	   r   r"      s    zUnitsMapping.__init__c                 C   s   dd t | j| jD | _d S )Nc                 S   s   i | ]\}\}}||qS r	   r	   )r   Z
input_nameZ
input_unit_r	   r	   r   r     r   z/UnitsMapping._rebuild_units.<locals>.<dictcomp>)zipr%   r+   _input_unitsr0   r	   r	   r   rM     s    zUnitsMapping._rebuild_unitsc                 C   s
   t | jS r/   r   r'   r0   r	   r	   r   r,     s    zUnitsMapping.n_inputsc                 C   s
   t | jS r/   rQ   r0   r	   r	   r   r1     s    zUnitsMapping.n_outputsc                    s   t  jS r/   )r!   r%   r0   r-   r	   r   r%     s    zUnitsMapping.inputsc                    s"   t t| jj| | |   d S r/   )r!   r   r.   r%   fsetrM   r*   valuer-   r	   r   r%     s    c                    s   t  jS r/   )r!   r&   r0   r-   r	   r   r&      s    zUnitsMapping.outputsc                    s"   t t| jj| | |   d S r/   )r!   r   r.   r&   rR   rM   rS   r-   r	   r   r&   $  s    c                 C   s   | j S r/   )rP   r0   r	   r	   r   input_units)  s    zUnitsMapping.input_unitsc                 C   s   | j S r/   r2   r0   r	   r	   r   r+   -  s    zUnitsMapping.mappingc                 G   sz   g }t || jD ]J\}\}}t|tr.|j}n|}|d u rF|| q|t||dd q| jdkrn|d S t|S d S )NT)Zsubokr   r   )rO   r+   
isinstancer   rT   appendr1   r#   )r*   r8   r:   argrN   Zreturn_unitrT   r	   r	   r   r;   1  s    

zUnitsMapping.evaluatec                 C   s2   | j d u rd| j dS d| j d| j dS d S )Nz<UnitsMapping(r3   r4   r5   r0   r	   r	   r   r6   B  s    
zUnitsMapping.__repr__)NFNN)rB   rC   rD   rE   r"   rM   rG   r,   r1   r%   setterr&   rU   r+   r;   r6   rH   r	   r	   r-   r   r      s2   @    



r   N)
rE   corer   r   Zastropy.unitsr   __all__r   r   r   r	   r	   r	   r   <module>   s   r5