a
    SicY                     @   s*  d Z ddlZddlZddlZddlZddlm  mZ ddl	m
Z ddl	mZ ddl	mZ ddlmZ ddlmZ ddlmZ g d	Ze ad
d Zedg dG dd dejjZejj ej_ edg deddejdd Zdd Zdd Z dd Z!dd Z"dd Z#dd Z$d d! Z%d"d# Z&dS )$z5Library for map layout and corresponding tf.Variable.    N)dtensor_api)lazy_variable)utils)
base_layer)
deprecated)keras_export)_self_tracked_trackables_trainable_weights_non_trainable_weights_captured_weight_regularizerc                   C   s   t tdd S )N
layout_map)getattr_LAYOUT_MAP r   r   T/var/www/html/django/DPS/env/lib/python3.9/site-packages/keras/dtensor/layout_map.pyget_current_layout_map1   s    r   z$keras.dtensor.experimental.LayoutMap)v1c                   @   sR   e Zd ZdZdddZdd Zdd Zd	d
 Zdd Zdd Z	dd Z
dd ZdS )	LayoutMapa  A dict-like object that maps string to `Layout` instances.

    `LayoutMap` uses a string as key and a `Layout` as value. There is a
    behavior difference between a normal Python dict and this class. The string
    key will be treated as a regex when retrieving the value. See the docstring
    of `get` for more details.

    See below for a usage example. You can define the naming schema
    of the `Layout`, and then retrieve the corresponding `Layout` instance.

    To use the `LayoutMap` with a `Model`, please see the docstring of
    `tf.keras.dtensor.experimental.layout_map_scope`.

    ```python
    map = LayoutMap(mesh=None)
    map['.*dense.*kernel'] = layout_2d
    map['.*dense.*bias'] = layout_1d
    map['.*conv2d.*kernel'] = layout_4d
    map['.*conv2d.*bias'] = layout_1d

    layout_1 = map['dense_1.kernel']    #   layout_1 == layout_2d
    layout_2 = map['dense_1.bias']      #   layout_2 == layout_1d
    layout_3 = map['dense_2.kernel']    #   layout_3 == layout_2d
    layout_4 = map['dense_2.bias']      #   layout_4 == layout_1d
    layout_5 = map['my_model/conv2d_123/kernel']    #   layout_5 == layout_4d
    layout_6 = map['my_model/conv2d_123/bias']      #   layout_6 == layout_1d
    ```

    Args:
      mesh: An optional `Mesh` that can be used to create all replicated
        layout as default when there isn't a layout found based on the input
        string query.
    Nc                 C   s   t  | _|| _d S N)collectionsOrderedDict_layout_map_default_mesh)selfmeshr   r   r   __init__Y   s    
zLayoutMap.__init__c                 C   s>   || j v r| j | S | j D ]}t||r| j |   S qdS )a  Retrieve the corresponding layout by the string key.

        When there isn't an exact match, all the existing keys in the layout map
        will be treated as a regex and map against the input key again. The
        first match will be returned, based on the key insertion order. Return
        None if there isn't any match found.

        Args:
          key: the string key as the query for the layout.

        Returns:
          Corresponding layout based on the query.
        N)r   rematch)r   keykr   r   r   __getitem__]   s    


zLayoutMap.__getitem__c                 C   sT   || j v r$t| d| j |  dt|tjsFt| dt| || j |< d S )Nz+ already exist in the LayoutMap with value z.. Please make sure to not use duplicated keys.z& should be a dtensor.Layout type, got )r   
ValueError
isinstancedtensorLayouttype)r   r   layoutr   r   r   __setitem__s   s    
zLayoutMap.__setitem__c                 C   s   | j |S r   )r   pop)r   r   r   r   r   __delitem__   s    zLayoutMap.__delitem__c                 C   s
   t | jS r   )lenr   r   r   r   r   __len__   s    zLayoutMap.__len__c                 C   s
   t | jS r   )iterr   r+   r   r   r   __iter__   s    zLayoutMap.__iter__c                 C   s   | j S )zReturn the default `Mesh` set at instance creation.

        The `Mesh` can be used to create default replicated `Layout` when there
        isn't a match of the input string query.
        )r   r+   r   r   r   get_default_mesh   s    zLayoutMap.get_default_meshc                 C   s   t | S )a  Apply layout to all `tf.Variable` instances created under the scope.

        All `tf.Variable` instances created under this scope
        will be lazily initialized first. Once they are attached as the model
        or layer attributes, and there is a stable layout mapping for it, the
        variables will be reinitialized into a
        `tf.experimental.dtensor.DVariable` with corresponding layout.

        Note that the layout mapping will use object/attribute names as the
        keys to map the variable to the layout.

        For subclassed models, the full object/attribute name is used as the
        key. For Functional/Sequential models, we use `layer.name` as
        the key for the layer, followed by the attribute name. Keras ensures
        name uniqueness among the layers within a Functional/Sequential model.

        See the following examples that show variable object names
        for different Keras model types:

        ```python
        layout_map = layout_map_lib.LayoutMap(mesh=self.mesh)
        layout_map['d1.kernel'] = layout_1
        layout_map['d1.bias'] = layout_2
        layout_map['d2.kernel'] = layout_3
        layout_map['d2.bias'] = layout_4

        ## Subclassed model
        class SubclassModel(tf.keras.Model):

          def __init__(self, name=None):
            super().__init__(name=name)
            self.d1 = tf.keras.layers.Dense(1000)
            self.d2 = tf.keras.layers.Dense(1000)

          def call(self, inputs):
            x = self.d1(inputs)
            return self.d2(x)

        with layout_map.scope():
          model = SubclassModel()
        inputs = tf.zeros((10, 10))
        results = model(inputs)

        model.d1.kernel.layout == layout_1
        model.d1.bias.layout == layout_2
        model.d2.kernel.layout == layout_3
        model.d2.bias.layout == layout_4

        ## Functional model
        with layout_map.scope():
          inputs = tf.keras.Input((10,), batch_size=10)
          x = tf.keras.layers.Dense(20, name='d1')(inputs)
          output = tf.keras.layers.Dense(30, name='d2')(x)

          model = tf.keras.Model(inputs, output)

        d1 = model.layers[1]
        d2 = model.layers[2]

        d1.kernel.layout == layout_1
        d1.bias.layout == layout_2
        d1.kernel.layout == layout_3
        d1.bias.layout == layout_4

        ## Sequential model
        with layout_map.scope():
          model = tf.keras.Sequential([
              tf.keras.layers.Dense(20, name='d1', input_shape=(10,)),
              tf.keras.layers.Dense(30, name='d2')
          ])

        d1 = model.layers[0]
        d2 = model.layers[1]

        d1.kernel.layout == layout_1
        d1.bias.layout == layout_2
        d1.kernel.layout == layout_3
        d1.bias.layout == layout_4
        ```

        Returns:
          A context that will lazily initialize all `tf.Variable` objects
          within the model, with their attributed layouts.
        )layout_map_scoper+   r   r   r   scope   s    UzLayoutMap.scope)N)__name__
__module____qualname____doc__r   r    r'   r)   r,   r.   r/   r1   r   r   r   r   r   5   s   "
r   z+keras.dtensor.experimental.layout_map_scopez<use tf.keras.dtensor.experimental.LayoutMap.scope() instead.c              	   c   sR   t  }| t_t * zdV  W |t_n|t_0 W d   n1 sD0    Y  dS )a  Apply the layout to all the tf.Variables created under the scope.

    Create a scope that all the tf.Variable created under this scope
    will be lazily inited, and initialized later on with proper layout when the
    object path in the model is stable/finalized.

    Note that the layout mapping will use the object/attribute names as the key
    to map the variable against the layout.

    For subclassed models, the full object/attribute name is used as the key.
    For Functional/Sequential models, since the layers within the model do not
    get assigned to a meaningful attribute, we use `layer.name` as the key for
    the layer, followed by the attribute name. Keras ensures name uniqueness
    among the layers in all Functional/Sequential models.

    See the following examples that show the variable object names
    for different Keras model types:

    ```python
    layout_map = layout_map_lib.LayoutMap(mesh=self.mesh)
    layout_map['d1.kernel'] = layout_1
    layout_map['d1.bias'] = layout_2
    layout_map['d2.kernel'] = layout_3
    layout_map['d2.bias'] = layout_4

    ## Subclassed model
    class SubclassModel(tf.keras.Model):

      def __init__(self, name=None):
        super().__init__(name=name)
        self.d1 = tf.keras.layers.Dense(1000)
        self.d2 = tf.keras.layers.Dense(1000)

      def call(self, inputs):
        x = self.d1(inputs)
        return self.d2(x)

    with layout_map_scope(layout_map):
      model = SubclassModel()
    # Triggering the creation of weights within or outside of the scope works
    inputs = tf.zeros((10, 10))
    results = model(inputs)

    model.d1.kernel.layout == layout_1
    model.d1.bias.layout == layout_2
    model.d2.kernel.layout == layout_3
    model.d2.bias.layout == layout_4

    ## Functional model
    with layout_map_scope(layout_map):
      inputs = tf.keras.Input((10,), batch_size=10)
      x = tf.keras.layers.Dense(20, name='d1')(inputs)
      output = tf.keras.layers.Dense(30, name='d2')(x)

      model = tf.keras.Model(inputs, output)

    d1 = model.layers[1]
    d2 = model.layers[2]

    d1.kernel.layout == layout_1
    d1.bias.layout == layout_2
    d1.kernel.layout == layout_3
    d1.bias.layout == layout_4

    ## Sequential model
    with layout_map_scope(layout_map):
      model = tf.keras.Sequential([
          tf.keras.layers.Dense(20, name='d1', input_shape=(10,)),
          tf.keras.layers.Dense(30, name='d2')
      ])

    d1 = model.layers[0]
    d2 = model.layers[1]

    d1.kernel.layout == layout_1
    d1.bias.layout == layout_2
    d1.kernel.layout == layout_3
    d1.bias.layout == layout_4
    ```

    Args:
      layout_map: a LayoutMap which contains the variable_object_path (string)
        -> Layout. When a layout is not found for the variable, a default all
        replicated layout will be created for the variable.

    Yields:
      A context that will lazily initialize all `tf.Variable` objects
      within the model, with their attributed layouts.
    N)r   r   r   r   Zlazy_init_scope)r   Zprevious_layout_mapr   r   r   r0      s    _
r0   c                    s   i }| j tddD ]T\ } fddtD r.qddd  D }t|||}t|  | ||t|< q| j dd d	D ]}t|| qx| j tddD ] \ }|t| }t|  | qt| | t	| | | S )
z0Map/Replace LazyInitVariable for subclass model.T	predicate	with_pathc                    s   g | ]}| v r|qS r   r   .0apathr   r   
<listcomp>e      z0_map_subclass_model_variable.<locals>.<listcomp>.c                 S   s   g | ]}t |qS r   strr:   itemr   r   r   r>   h  r?   c                 S   s   t | tjS r   )r"   r   Layeror   r   r   <lambda>o  r?   z._map_subclass_model_variable.<locals>.<lambda>r7   )
_flatten_is_lazy_init_variable_KERAS_ATTRIBUTES_TO_SKIPjoin_create_dvariable_set_object_by_pathid _config_dvariable_regularization_init_state_variable_for_rng_update_trackable_reference)modelr   %lazy_init_variable_to_tf_variable_mapvariableobject_pathnew_variablelayertf_variabler   r<   r   _map_subclass_model_variableY  s4    


r[   c           	         s   i }| j D ]}|j}|jtddD ]`\ } fddtD r>q"ddd  D }|d | }t|||}t| | ||t|< q"t	|| |jtddD ] \ }|t| }t| | qq
t
| | t| | | S )z=Map/Replace LazyInitVariable for functional/sequential model.Tr6   c                    s   g | ]}| v r|qS r   r   r9   r<   r   r   r>     r?   z2_map_functional_model_variable.<locals>.<listcomp>r@   c                 S   s   g | ]}t |qS r   rA   rC   r   r   r   r>     r?   )layersnamerJ   rK   rL   rM   rN   rO   rP   rQ   rR   rS   )	rT   r   rU   rY   
layer_namerV   rW   rX   rZ   r   r<   r   _map_functional_model_variable  s4    


r_   c              	   C   s   | j dd dD ]}|j}|jr2|jdu r2tdt|dr^t|jjr^t|d|jj|j_qt	
|  |  W d   q1 s0    Y  qdS )aI  Init the state variable in tf.ranodm.Generator.

    Since the BaseRandomLayer in keras explicitly untrack the
    tf.random.Generator, the variable in it will stay as LazyInitVariable, which
    cause runtime error if we don't replace them with proper DVariable. Since
    user usually are not aware the existence of those variable, we will just
    give them replicated layout since they are tiny.

    Args:
      model: the model whose layers will be checked to find the
        BaseRandomLayers.
      layout_map: used to get the default mesh information to create DVariable.
    c                 S   s   t | tjS r   )r"   r   ZBaseRandomLayerrF   r   r   r   rH     r?   z._init_state_variable_for_rng.<locals>.<lambda>rI   NzKeras is expected to use tf.random.Generator when using DTensor API. Please call `tf.keras.backend.experimental.enable_tf_random_generator` at the beginning of your program.
_generator )rJ   _random_generator_builtr`   r!   hasattrrK   
_state_varrN   r#   run_onr/   _maybe_init)rT   r   lZkeras_generatorr   r   r   rR     s     


rR   c                 C   sL   | j D ]:\}}}t|s&td| |t| }| ||| qg | _ dS )a`  Update the weights regularizer for newly created `DVariable`.

    The weight regularization usually happens when `layer.add_weight()` is
    called, at which point the library will first create a `LazyInitVariable`,
    and then replace it with a `DVariable`. We will defer the creation of those
    losses, until the DVariable is created.

    See `layer._captured_weight_regularizer` for more details.

    Args:
      layer: the layer instance for DVariable regularization config.
      lazy_init_variable_to_tf_variable_map: the dict between LazyInitVariable
        ID and newly created DVariable.
    zFExpect the regularization loss are created from LazyInitVariable, got N)r   rK   r!   rP   _handle_weight_regularization)rY   rU   r]   rV   Zregualarizer
d_variabler   r   r   rQ     s    rQ   c                 C   s   | | }|du r,|j j}tjj|  |d}|j}t|rpt	  t
||}W d   q|1 sd0    Y  nt||}|j}|dr|dd }tj||j|d}|S )a  Create a new variable instead of using the LazyInitVariable.

    We choose to do this since even the LazyInitVariable might behavior like
    a normal tf.Variable/DVariable, it is not future proof for any new changes
    to variable class. It will also fail the instance type check in python,
    which could affect user's code when they do any filtering based on type to
    find any variables.

    Args:
      layout_map: a LayoutMap which contains the variable_object_path (string)
        -> Layout.
      object_path: string, the object attribute path for the variable.
      variable: LazyInitVariable which will be replaced by the newly created
        tf.Variable.
    Returns:
      A new tf.Variable with correct layout information.
    N)r   rankz:0)	trainabler]   )shaperk   r#   r$   
replicatedr/   _initial_valuecallabler   Zdisable_init_variable_creatorr   call_with_layoutcopy_to_meshr]   endswith	DVariablerm   )r   rW   rV   r&   Zvariable_rankinit_valvariable_namerX   r   r   r   rN     s$    
,
rN   c                 C   sf   t |D ]X\}}|t|d krBt|tr4|| |< q`t| || qt|trV| | } qt| |} qdS )a5  Set the attribute of instance to the object.

    Args:
      object_to_set: the instance whose attribute should be set.
      path: the tuple/list of string and ints, representing the attribute names.
        Int means that the attribute to set is a item a list.
      value: the value of the attribute.
       N)	enumerater*   r"   intsetattrr   )Zobject_to_setr=   valuei	attr_namer   r   r   rO   $  s    




rO   c                 C   s^   t jj| }| \}}|D ]:}|  D ](\}}t|r.|j|t	| |dd q.qdS )a  Update the trackable object references for the model.

    Note that this method is only needed because of a corner case for model
    checkpoint, where it could accidently catch a LazyInitVariable in checkpoint
    dependency and not visible to the model attribute graph itself.

    Args:
      model: the keras model instance whose checkpoint dependency will be
        examed.
      lazy_init_variable_to_tf_variable_map: the dict between LazyInitVariable
        ID and newly created DVariable.
    T)	overwriteN)
tf__internal__trackingObjectGraphViewbreadth_first_traversal_trackable_childrenitemsrK   _track_trackablerP   )rT   rU   Zobject_graph
trackables_	trackableZref_namerefr   r   r   rS   ?  s    
rS   c                 C   s   t | tjS r   )r"   r   ZLazyInitVariable)objr   r   r   rK   Z  s    rK   )'r5   r   
contextlibr   	threadingtensorflow.compat.v2compatv2r   keras.dtensorr   r#   r   r   keras.enginer   "tensorflow.python.util.deprecationr    tensorflow.python.util.tf_exportr   rL   localr   r   abcMutableMappingr   r    getcontextmanagerr0   r[   r_   rR   rQ   rN   rO   rS   rK   r   r   r   r   <module>   s@   
 7
e).)-