
    AVhZ                         d 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mZ  G d d	e      Zdd
ZddZ	 	 ddZ	 	 	 ddZ	 	 	 ddZ	 	 ddZd Zd Zd Z	 	 ddZ	 	 ddZ	 ddZ	 	 	 ddZy)z0Experimental support for defining XLA shardings.    N)xla)xla_data_pb2)attr_value_pb2)context)resource_variable_opsc                       e Zd ZdZddZed        Zed        Zed        Zed        Z	ed        Z
ed	        Zedd
       Z	 	 	 ddZd Zed        Zd Zd Zy)ShardingzA class to support adding sharding attributes to Ops.

  Use the factory constructors and then call apply_to_tensor:
    Sharding.replicate().apply_to_tensor(tensor)
  Nc                     || _         y)z=Do not use this constructor; use the factory functions below.N_proto)selfprotos     h/home/dcms/DCMS/lib/python3.12/site-packages/tensorflow/python/compiler/xla/experimental/xla_sharding.py__init__zSharding.__init__!   s	    DK    c                 r    t        t        j                  t        j                  j                              S )zReturns a replicated sharding attribute.

    This causes an op to be computed in its entirety independently on all
    cores in the XLA device.
    typer   )r	   r   
OpSharding
REPLICATEDclss    r   	replicatezSharding.replicate%   s-     %%<+B+B+M+MNP Pr   c                 r    t        t        j                  t        j                  j                              S )zReturns a manuall sharding attribute.

    This means the op is manually partitioned by the user and XLA will not
    change the shapes.
    r   r   )r	   r   r   MANUALr   s    r   manualzSharding.manual/   s-     %%<+B+B+I+IJL Lr   c                 z    t        t        j                  t        j                  j                  dg|g            S )zReturns an AssignDevice sharding attribute.

    This causes an op to be computed in its entirety only on one core in
    the XLA device.
    Args:
      core: The core to assign this Op to.
       r   tile_assignment_dimensionstile_assignment_devicesr   )r	   r   r   MAXIMAL)r   cores     r   assign_devicezSharding.assign_device9   s8     %%((00()s%)F,- -r   c           	      "   t        |t        j                        st        d      t	        |j
                        }|j                  dd      }t        t        j                  t        j                  j                  |t	        |                  S )a  Returns a Tiled sharding attribute.

    This causes an op to be partially computed on multiple cores in the
    XLA device.

    Args:
      tile_assignment: An np.ndarray describing the topology of the tiling and
        which device will compute which part of the topology.

    Raises:
      TypeError: tile_assignment was not of np.array type.

    TODO(jmolloy): This concept is nefarious and is not
    something we really want to expose to users (especially as the
    contract for tile_assignment is very strict).
    z*Tile assignment must be of type np.ndarrayCorderr    r   
isinstance_npndarray	TypeErrorlistshapereshaper	   r   r   OTHERr   tile_assignmentdimsflattened_devicess       r   tilezSharding.tileH   sz    $ os{{3BCC%%&D'//#/>%%((..'+$():$;=> >r   c           
      T   t        |t        j                        st        d      t        |t              st        d      t        |j                        t        |      k  rt        d      |D ]C  }|t        j                  j                  t        j                  j                  fvs:t        d       t	        |j                        }|j                  dd      }t        t        j                  t        j                  j                  |t	        |      t	        |            	      S )
ai  Returns a subgroup manual sharding attribute.

    This is similar to tile(), but tile_assignment has one or more dimension
    than the tensor, and subgroup_modes define the sharding types in the last
    dimensions of tile_assignment.

    Args:
      tile_assignment: An np.ndarray describing the topology of the tiling and
        which device will compute which part of the topology.
      subgroup_modes: sharding types for the dimension more than the tensor
        shape rank.

    Raises:
      TypeError: tile_assignment was not of np.array type or subgroup_modes
        has unsupported sharding type.
    z2SubgroupTile assignment must be of type np.ndarrayz6subgroup_modes in subgroup manual must be of type listzKSubgroupTile assignment must have rank larger than length of subgroup_modeszEach sharding_type in subgroup_modes in subgroup manual must be of type xla_data_pb2.OpSharding.REPLICATED or xla_data_pb2.OpSharding.MANUALr'   r(   r)   )r   r!   r"   last_tile_dimsr   )r,   r-   r.   r/   r0   lenr1   r   r   r   r   r2   r	   r3   )r   r5   subgroup_modessharding_typer6   r7   s         r   subgroup_tilezSharding.subgroup_tiled   s   $ os{{3JKKnd+NOO
?  !C$77 2 3 3 ( 2	

!
!
,
,l.E.E.L.L 
 12 	2	2 %%&D'//#/>%%((..'+$():$;/	12 2r   c           	      $   t        |t        j                        st        d      t	        |j
                        }|j                  dd      }t        t        j                  t        j                  j                  |t	        |      d            S )a  Returns a partially tiled sharding attribute.

    This is similar to tile(), but tile_assignment has one more dimension than
    the tensor, and tiles in the last dimension of tile_assignment are
    replicated.

    Args:
      tile_assignment: An np.ndarray describing the topology of the tiling and
        which device will compute which part of the topology.

    Raises:
      TypeError: tile_assignment was not of np.array type.
    z1PartialTile assignment must be of type np.ndarrayr'   r(   r)   T)r   r!   r"   replicate_on_last_tile_dimr   r+   r4   s       r   partial_tilezSharding.partial_tile   s}     os{{3IJJ%%&D'//#/>%%((..'+$():$;'+	-. .r   c           	      0   |r|}n|j                   j                         }||   ||   |k  rt        d|d|d|      dgt        |      z  }|||<   t	        t        j                  t
        j                  j                  |t        |                  S )a/  Returns a Sharding that splits a tensor across a dimension.

    This creates a Tiled attribute, similar to tile(), but easier to use for the
    common case of tiling a tensor N ways in one dimension.

    Args:
      tensor: A tf.Tensor to split.
      split_dimension: The dimension number to split.
      num_devices: The number of cores to split `tensor` over.
      input_shape: The shape of the original tensor.

    Raises:
      ValueError: The tensor to split was smaller in the split dimension than
        the number of devices to split over.
    zFSplit dimension was smaller than the required number of splits: shape=z, dimension=z, num_devices=r   r    r   )	r1   as_list
ValueErrorr;   r	   r   r   r3   range)r   tensorsplit_dimensionnum_devicesinput_shaper1   tile_assignment_dimss          r   splitzSharding.split   s    " ell""$eo*o,= > > 3U+,7)%%((..';$)+$689 9r   c                 Z   |r|r|rJ | j                   }t        |t        j                        r;t	        j
                         r'|r| j                  d      }|j                  |       |S |re|r8| j                  d      }t        j                  ||j                               }nt        j                  ||j                         |xs g       }n|s"t        |j                  j                        dkD  rx| j                  |j                        }t        |j                         }| j                   ||j"                  <   t%        j&                  t$        j&                  j(                  |      }|j                  j+                  dt-        j.                  |j                                      |S )ae  Applies this Sharding attribute to `tensor`.

    Args:
      tensor: A tf.Tensor to split.
      assign_tuple_sharding: If the sharding type should be a tuple.
      use_sharding_op: Whether to create a sharding op on `tensor`.
      unspecified_dims: An optional list of dimensions unspecified.

    Returns:
      The tensor with Sharding attribute.
    r   )num_outputssharding)rO   unspecified_dimsr   tuple_shardings_XlaShardings)r   r,   r   BaseResourceVariabler   +xla_sharding_for_resource_variables_enabled_create_tuple_proto_set_xla_shardingtf2xlarO   SerializeToStringr;   opoutputs_get_or_create_tuple_protor0   rR   value_indexr   r   TUPLE	_set_attrr   	AttrValue)r   rF   assign_tuple_shardinguse_sharding_oprP   r   rR   s          r   apply_to_tensorzSharding.apply_to_tensor   se     %:::KKE 	60EEF??A	((Q(7u%m	((Q(7%2I2I2KL,,.-35 
#fii&7&7"81"<--fii8e U223o,0KKof(()%%&&,,oOe II&0053J3J3LMOMr   c                     t        j                  | j                  j                               }|j	                  d|       y)z{Applies this Sharding attribute to `operation`.

    Args:
      operation: A tf.Operation to add sharding annotation.
    rT   rS   N)r   rb   r   r[   ra   )r   	operation
attr_values      r   apply_to_operationzSharding.apply_to_operation
  s1      ))DKK,I,I,KLJ
3r   c                     | j                   S )z=Return the sharding protobuf of type xla_data_pb2.OpSharding.r   )r   s    r   r   zSharding.proto  s     ;;r   c                     	 |j                  d      }t        j                         }|j                  |       |S # t        $ r' | j                  t        |j                              cY S w xY w)NrS   )get_attrr   r   ParseFromStringrD   rX   r;   r]   )r   r\   attrr   s       r   r^   z#Sharding._get_or_create_tuple_proto  s^    7[[(d%%'eD!l 7%%c"**o667s   7: -A*)A*c                     t        j                  t         j                  j                        g|z  }t        j                  t         j                  j                  |      S )Nr   rQ   )r   r   r   r`   )r   rM   	shardingss      r   rX   zSharding._create_tuple_proto"  sS    \%<%<%G%GHI ""$$**IG Gr   NFFN)__name__
__module____qualname____doc__r   classmethodr   r   r%   r8   r>   rA   rK   re   ri   propertyr   r^   rX    r   r   r	   r	      s     P P L L - - > >6 *2 *2X . .2 !9 !9J -2&+'+	9v4  7Gr   r	   c                    t        |       }||S t        |t        j                        rLt	        j
                         r8t        j                         }|j                  |       |j                  |       |S |rt        j                  ||      }t        j                  |      }|j                  j                  d|       |S )a@  Copies the a tensor's sharding to another.

  Args:
    from_tensor: Source tensor. Must be the sole output of an op.
    to_tensor: the tensor the annotate with the copy.
    use_sharding_op: whether to create a sharding op on `to_tensor`.

  Returns:
    A tensor with sharding annotation copied from `from_tensor`.
  rN   rT   rS   )get_tensor_shardingr,   r   rV   r   rW   r   r   rm   rY   rZ   rO   r   rb   r\   ra   )from_tensor	to_tensorrd   rO   r   rh   s         r   copy_shardingr~   *  s     !-( 1FFG

=
=
?##%E	(#&	H=I''(3*,,4	r   c                 N    t         j                         j                  | ||      S )Nrc   rd   )r	   r   re   )rF   rc   rd   s      r   r   r   S  s,    					-	-1% 
. 
' 'r   c                 P    t         j                  |      j                  | ||      S )z:Returns a tensor that has AssignDevice sharding attribute.r   )r	   r%   re   )rF   devicerc   rd   s       r   r%   r%   Z  s0    
 
			'	7	71% 
8 
' 'r   c                 Z    t         j                  |      j                  | |||xs g       S )a  Returns a tensor that has tiled sharding.

  Args:
    tensor: A tf.Tensor to shard.
    tile_assignment: An np.ndarray describing the topology of the tiling and
      which device will compute which part of the topology.
    assign_tuple_sharding: If the sharding type should be a tuple.
    use_sharding_op: If true, adds a sharding op to set the sharding.
    unspecified_dims: An optional list of dimensions unspecified.
  )rc   rd   rP   )r	   r8   re   )rF   r5   rc   rd   rP   s        r   r8   r8   e  s6     
	'	7	71%'-2	 
8 
/ /r   c                 V    t         j                  | |||      j                  | ||      S )a  Returns a tensor that is split along the given dimension.

  Args:
    tensor: A tf.Tensor to split.
    split_dimension: The dimension to split.
    num_devices: The number of devices to partition the dimension.
    assign_tuple_sharding: If the sharding type should be a tuple.
    use_sharding_op: If true, adds a sharding op to set the sharding.
    input_shape: The full shape of the input tensor.
  r   )r	   rK   re   )rF   rG   rH   rc   rd   rI   s         r   rK   rK   {  s6      
#
%%4_"2G,; &5 &==r   c                 X    t         j                  |      j                  | ||xs g       S )a  Returns a tensor that has tiled sharding.

  Args:
    tensor: A tf.Tensor to shard.
    tile_assignment: An np.ndarray describing the topology of the tiling and
      which device will compute which part of the topology. It must have one
      more dimension than tensor, and the last dimension represents partially
      replicated tiles.
    use_sharding_op: If true, adds a sharding op to set the sharding.
    unspecified_dims: An optional list of dimensions unspecified.
  rd   rP   )r	   rA   re   )rF   r5   rd   rP   s       r   rA   rA     s6     
			/	?	?%'-2 
@ 
/ /r   c                 X    	 | j                  d      S # t        $ r Y yt        $ r Y yw xY w)zReturns sharding attribute of an op.

  Args:
    op: a TensorFlow op.

  Returns:
    The attribute representing XLA sharding on this op.
  rS   N)rl   rD   AttributeError)r\   s    r   get_op_shardingr     s3    ;;~&&	 	 s    	)))c                     t        | t        j                        r7t        j                         r#| j                         }|y|j                         S 	 t        | j                        S # t        $ r Y yw xY w)zReturns sharding attribute of a Tensor.

  Args:
    tensor: a Tensor.

  Returns:
    The attribute representing XLA sharding on tensor's op.
  N)
r,   r   rV   r   rW   _get_xla_shardingr[   r   r\   r   )rF   rO   s     r   r{   r{     so     .CCD

=
=
? '')H''))699%%	 s   A( (	A43A4c                     | yt        j                         }|j                  |        |j                  r|j                  S y)a_  Returns the tile assignment shape for a sharded Tensor.

  Args:
    sharding: a serialized OpSharding message describing the layout of a
      sharded Tensor.

  Returns:
    A list, for each dimension of the sharded Tensor, of the number of shards
      into which it has been split. Returns None if the input indicates no tile
      assignments.
  N)r   r   rm   r!   )rO   sharding_messages     r   get_sharding_tile_shaper     sB     !,,.""8,00666r   c                 <    t        j                  | |||xs g       S )aa  Switches from automatic SPMD partitioning to manual partitioning.

  Converts a full-shaped tensor (to be automatically partitioned by SPMD
  partitioner) to a shard-shaped tensor to be consumed by manually partitioned
  ops.

  Args:
    tensor: A tf.Tensor in full shape.
    manual_sharding: A serialized string of OpSharding to be used in manual
      partitioning.
    single_dim: If >= 0, the conversion will happen only on this dim in
      subgroups.
    unspecified_dims: An optional list of dimensions unspecified.

  Returns:
    A shard-shaped tensor to be consumed by manually partitioned ops.
  )manual_shardingdimrP   )rZ   spmd_full_to_shard_shape)rF   r   
single_dimrP   s       r   auto_to_manual_spmd_partitionr     s(    * 
	(	(%
'-2	
/ /r   c                 >    t        j                  | ||||xs g       S )a  Switches from manual partitioning to automatic SPMD partitioning.

  Converts a shard-shaped tensor (manually partitioned in SPMD-style) to a
  full-shaped tensor to be partitioned automatically by the SPMD partitioner.

  Args:
    tensor: A tf.Tensor in shard shape.
    manual_sharding: a serialized string of OpSharding to be used in manual
      partitioning.
    full_shape: the shape of tensor before partitioning.
    single_dim: If >= 0, the conversion will happen only on this dim in
      subgroups.
    unspecified_dims: An optional list of dimensions unspecified.

  Returns:
    A full-shaped tensor to be partitioned automatically by the SPMD
    partitioner.
  )r   
full_shaper   rP   )rZ   spmd_shard_to_full_shape)rF   r   r   r   rP   s        r   manual_to_auto_spmd_partitionr   	  s+    . 
	(	(%
'-2
/ /r   c           
         |xs g }|D cg c]
  }|dk\  s	| c}|z   }t        |      t        | j                        kD  r=t        dt        |      dt        | j                        d|d| j                        |t        t        | j                              D cg c]	  }||vs| c}z   }t	        j
                  | |      }||z   D cg c]  }|dk  rdn| j                  |    }}t        j                  j                  gt        |      z  }t        |      t        | j                        k  }	|	rm|j                  t	        j                  | j                        t	        j                  |      z         |j                  t        j                  j                         t	        j                  ||      }|rt        j                  ||      S |	rt        j                  |      S t        j!                  |      S c c}w c c}w c c}w )a  Returns a Sharding object representing sharding along multiple dimensions.

  Args:
    device_mesh: An np.ndarray describing the topology of the device mesh and
      each element is the ID of the device in the topology.
    tensor_split_dims_mapping: A list of integers that map each tensor axis to
      the device mesh axis along which it is sharded. Its length is the tensor
      rank, and tensor_split_dims_mapping[i] is device mesh axis for tensor
      dimension i. Use -1 for tensor dimensions that are not sharded.
    manual_mesh_dims: An optional list of mesh dims for manual subgroups.

  Raises:
    ValueError: The number of tensor split dimensions is larger than device mesh
      rank.
  r   z#Number of tensor split dimensions (z#) is larger than device mesh rank (z). tensor_split_dims_mapping: z, device_mesh.shape: r   )r;   r1   rD   rE   r-   	transposer   r   r   appendprodr   r2   r	   r>   rA   r8   )
device_meshtensor_split_dims_mappingmanual_mesh_dimsdpermutationtranspose_permutationr5   
tile_shaper<   partials
             r   mesh_split_shardingr   (  s   $ &+5 qa $%+K--..
 
[	3 9;;L;L	NO O &s;,,-.)!;2Fa)  MM+/DE/ *,<<
 q5ak''***  !++223c:J6KK.s;#4#455'chh{001SXXj5IIJ,11<<=KK</!!/>BB  11		'';)s   
G?G?	H)HH	c                 L    t        |||      }|j                  | ||xs g       S )al  Returns a tensor that is split along multiple dimensions in a device mesh.

  Args:
    tensor: A tf.Tensor to split.
    device_mesh: An np.ndarray describing the topology of the device mesh and
      each element is the ID of the device in the topology.
    tensor_split_dims_mapping: A list of integers that map each tensor axis to
      the device mesh axis along which it is sharded. Its length is the tensor
      rank, and tensor_split_dims_mapping[i] is device mesh axis for tensor
      dimension i. Use -1 for tensor dimensions that are not sharded.
    use_sharding_op: If true, adds a sharding op to set the sharding.
    manual_mesh_dims: An optional list of mesh dims for manual subgroups.
    unspecified_dims: An optional list of dimensions unspecified.

  Raises:
    ValueError: The number of tensor split dimensions is larger than device mesh
      rank.
  r   )r   re   )rF   r   r   rd   r   rP   rO   s          r   
mesh_splitr   [  s<    0 !.G!13(		!	!%'-2 
" 
/ /r   )F)FFrr   )FN)r'   Nrq   )FNN)rv   numpyr-   !tensorflow.compiler.tf2xla.pythonr   rZ   tensorflow.compiler.xlar   tensorflow.core.frameworkr   tensorflow.python.eagerr   tensorflow.python.opsr   objectr	   r~   r   r%   r8   rK   rA   r   r{   r   r   r   r   r   ry   r   r   <module>r      s    7  ; 0 4 + 7MGv MG`"R' )."''  %	/2 !&=2 "'"&/*$<0 .037/> .037	/B *.0(l  % $ $/r   