Reference Guide  2.5.0
psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans Class Reference
Inheritance diagram for psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans:
Collaboration diagram for psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans:

Public Member Functions

def apply (self, node, options=None)
 
def __str__ (self)
 
def name (self)
 
def validate (self, node, options=None)
 

Detailed Description

Transformation that given an assignment with an ArrayReference Range
in the LHS (equivalent to an array assignment statement in Fortran), it
converts it to an explicit loop doing each of the individual element
assignments separately. For example:

>>> from psyclone.parse.algorithm import parse
>>> from psyclone.psyGen import PSyFactory
>>> api = "nemo"
>>> filename = "tra_adv.F90" # examples/nemo/code
>>> ast, invoke_info = parse(filename, api=api)
>>> psy = PSyFactory(api).create(invoke_info)
>>> schedule = psy.invokes.invoke_list[0].schedule
>>> print(schedule.view())
>>>
>>> from psyclone.psyir.nodes import Range
>>> from psyclone.domain.nemo.transformations import \
        NemoArrayRange2LoopTrans
>>> from psyclone.transformations import TransformationError
>>>
>>> trans = NemoArrayRange2LoopTrans()
>>> for my_range in reversed(schedule.walk(Range)):
>>>     try:
>>>         trans.apply(my_range)
>>>     except TransformationError:
>>>         pass
>>> print(schedule.view())

The specified Range node must be the outermost Range (specifying
an access to an array index) within an Array Reference and the
array reference must be on the left-hand-side of an Assignment
node. This is required for correctness and if not satisfied the
transformation will raise an exception.

By default the transformation will reject character arrays,
though this can be overriden by setting the
allow_string option to True. Note that PSyclone expresses syntax such
as `character(LEN=100)` as UnsupportedFortranType, and this
transformation will convert unknown or unsupported types to loops.

Definition at line 58 of file nemo_arrayrange2loop_trans.py.

Member Function Documentation

◆ apply()

def psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans.apply (   self,
  node,
  options = None 
)
 Apply the transformation such that, given an assignment with an
ArrayReference Range in the LHS (equivalent to an array assignment
statement in Fortran), it converts it to an explicit loop doing each
of the individual element assignments separately.

The Range node is provided to the apply method of the transformation
to indicate which array index should be transformed. This can only
be applied to the outermost Range of the ArrayReference.

:param node: a Range node.
:type node: :py:class:`psyclone.psyir.nodes.Range`
:param options: a dictionary with options for \
    transformations. No options are used in this \
    transformation. This is an optional argument that defaults \
    to None.
:type options: Optional[Dict[str, Any]]
:param bool options["allow_string"]: whether to allow the
    transformation on a character type array range. Defaults to False.

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 99 of file nemo_arrayrange2loop_trans.py.

99  def apply(self, node, options=None):
100  ''' Apply the transformation such that, given an assignment with an
101  ArrayReference Range in the LHS (equivalent to an array assignment
102  statement in Fortran), it converts it to an explicit loop doing each
103  of the individual element assignments separately.
104 
105  The Range node is provided to the apply method of the transformation
106  to indicate which array index should be transformed. This can only
107  be applied to the outermost Range of the ArrayReference.
108 
109  :param node: a Range node.
110  :type node: :py:class:`psyclone.psyir.nodes.Range`
111  :param options: a dictionary with options for \
112  transformations. No options are used in this \
113  transformation. This is an optional argument that defaults \
114  to None.
115  :type options: Optional[Dict[str, Any]]
116  :param bool options["allow_string"]: whether to allow the
117  transformation on a character type array range. Defaults to False.
118 
119  '''
120  self.validate(node, options)
121 
122  assignment = node.ancestor(Assignment)
123  parent = assignment.parent
124  # Ensure we always use the routine-level symbol table
125  symbol_table = node.ancestor(Routine).symbol_table
126 
127  # Create a new, unique, iteration variable for the new loop
128  loop_variable_symbol = symbol_table.new_symbol(root_name="idx",
129  symbol_type=DataSymbol,
130  datatype=INTEGER_TYPE)
131 
132  # Replace the loop_idx array dimension with the loop variable.
133  n_ranges = None
134  # Just loop the top-level arrays since we just do 1 substitution per
135  # array construct, even if they have nested arrays in turn.
136  for top_level_ref in assignment.walk(ArrayMixin, stop_type=ArrayMixin):
137  # Then start checking with the inner-most array
138  for array in reversed(top_level_ref.walk(ArrayMixin)):
139  current_n_ranges = len([child for child in array.children
140  if isinstance(child, Range)])
141  if current_n_ranges == 0:
142  continue # This sub-expression already has explicit dims
143  if n_ranges is None:
144  n_ranges = current_n_ranges
145  elif n_ranges != current_n_ranges:
146  raise InternalError(
147  "The number of ranges in the arrays within this "
148  "assignment are not equal. Any such case should have "
149  "been dealt with by the validation method or "
150  "represents invalid PSyIR.")
151 
152  idx = array.get_outer_range_index()
153  array.children[idx] = Reference(loop_variable_symbol)
154  break # If one is found, go to the next top level expression
155 
156  # Replace the assignment with the new explicit loop structure
157  position = assignment.position
158  start, stop, step = node.pop_all_children()
159  loop = Loop.create(loop_variable_symbol, start, stop, step,
160  [assignment.detach()])
161  parent.children.insert(position, loop)
162 

References psyclone.domain.lfric.kernel.lfric_kernel_metadata.LFRicKernelMetadata.validate(), psyclone.transformations.MoveTrans.validate(), psyclone.transformations.Dynamo0p3AsyncHaloExchangeTrans.validate(), psyclone.domain.common.transformations.alg_invoke_2_psy_call_trans.AlgInvoke2PSyCallTrans.validate(), psyclone.domain.common.transformations.alg_trans.AlgTrans.validate(), psyclone.domain.common.transformations.kernel_module_inline_trans.KernelModuleInlineTrans.validate(), psyclone.domain.common.transformations.raise_psyir_2_alg_trans.RaisePSyIR2AlgTrans.validate(), psyclone.domain.gocean.transformations.gocean_const_loop_bounds_trans.GOConstLoopBoundsTrans.validate(), psyclone.domain.gocean.transformations.gocean_move_iteration_boundaries_inside_kernel_trans.GOMoveIterationBoundariesInsideKernelTrans.validate(), psyclone.domain.gocean.transformations.gocean_opencl_trans.GOOpenCLTrans.validate(), psyclone.domain.gocean.transformations.raise_psyir_2_gocean_kern_trans.RaisePSyIR2GOceanKernTrans.validate(), psyclone.domain.lfric.transformations.lfric_alg_invoke_2_psy_call_trans.LFRicAlgInvoke2PSyCallTrans.validate(), psyclone.domain.lfric.transformations.raise_psyir_2_lfric_kern_trans.RaisePSyIR2LFRicKernTrans.validate(), psyclone.domain.nemo.transformations.create_nemo_invoke_schedule_trans.CreateNemoInvokeScheduleTrans.validate(), psyclone.domain.nemo.transformations.create_nemo_psy_trans.CreateNemoPSyTrans.validate(), psyclone.domain.nemo.transformations.nemo_allarrayrange2loop_trans.NemoAllArrayRange2LoopTrans.validate(), psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans.validate(), psyclone.domain.nemo.transformations.nemo_outerarrayrange2loop_trans.NemoOuterArrayRange2LoopTrans.validate(), psyclone.psyad.transformations.assignment_trans.AssignmentTrans.validate(), psyclone.psyGen.Transformation.validate(), psyclone.psyir.transformations.acc_update_trans.ACCUpdateTrans.validate(), psyclone.psyir.transformations.allarrayaccess2loop_trans.AllArrayAccess2LoopTrans.validate(), psyclone.psyir.transformations.arrayaccess2loop_trans.ArrayAccess2LoopTrans.validate(), psyclone.psyir.transformations.arrayrange2loop_trans.ArrayRange2LoopTrans.validate(), psyclone.psyir.transformations.chunk_loop_trans.ChunkLoopTrans.validate(), psyclone.psyir.transformations.fold_conditional_return_expressions_trans.FoldConditionalReturnExpressionsTrans.validate(), psyclone.psyir.transformations.hoist_local_arrays_trans.HoistLocalArraysTrans.validate(), psyclone.psyir.transformations.hoist_loop_bound_expr_trans.HoistLoopBoundExprTrans.validate(), psyclone.psyir.transformations.hoist_trans.HoistTrans.validate(), psyclone.psyir.transformations.inline_trans.InlineTrans.validate(), psyclone.psyir.transformations.intrinsics.array_reduction_base_trans.ArrayReductionBaseTrans.validate(), psyclone.psyir.transformations.intrinsics.dotproduct2code_trans.DotProduct2CodeTrans.validate(), psyclone.psyir.transformations.intrinsics.intrinsic2code_trans.Intrinsic2CodeTrans.validate(), psyclone.psyir.transformations.intrinsics.matmul2code_trans.Matmul2CodeTrans.validate(), psyclone.psyir.transformations.loop_swap_trans.LoopSwapTrans.validate(), psyclone.psyir.transformations.loop_tiling_2d_trans.LoopTiling2DTrans.validate(), psyclone.psyir.transformations.loop_trans.LoopTrans.validate(), psyclone.psyir.transformations.omp_task_trans.OMPTaskTrans.validate(), psyclone.psyir.transformations.omp_taskwait_trans.OMPTaskwaitTrans.validate(), psyclone.psyir.transformations.parallel_loop_trans.ParallelLoopTrans.validate(), psyclone.psyir.transformations.reference2arrayrange_trans.Reference2ArrayRangeTrans.validate(), psyclone.psyir.transformations.replace_induction_variables_trans.ReplaceInductionVariablesTrans.validate(), psyclone.transformations.OMPDeclareTargetTrans.validate(), psyclone.transformations.DynamoOMPParallelLoopTrans.validate(), psyclone.transformations.Dynamo0p3OMPLoopTrans.validate(), psyclone.transformations.GOceanOMPLoopTrans.validate(), psyclone.transformations.Dynamo0p3RedundantComputationTrans.validate(), psyclone.transformations.Dynamo0p3KernelConstTrans.validate(), psyclone.transformations.ACCRoutineTrans.validate(), psyclone.transformations.KernelImportsToArguments.validate(), psyclone.domain.gocean.transformations.gocean_loop_fuse_trans.GOceanLoopFuseTrans.validate(), psyclone.domain.lfric.transformations.lfric_loop_fuse_trans.LFRicLoopFuseTrans.validate(), psyclone.psyir.transformations.loop_fuse_trans.LoopFuseTrans.validate(), psyclone.domain.gocean.transformations.gocean_extract_trans.GOceanExtractTrans.validate(), psyclone.domain.lfric.transformations.lfric_extract_trans.LFRicExtractTrans.validate(), psyclone.psyir.transformations.extract_trans.ExtractTrans.validate(), psyclone.psyir.transformations.nan_test_trans.NanTestTrans.validate(), psyclone.psyir.transformations.read_only_verify_trans.ReadOnlyVerifyTrans.validate(), psyclone.transformations.ParallelRegionTrans.validate(), psyclone.transformations.OMPParallelTrans.validate(), psyclone.transformations.ACCParallelTrans.validate(), psyclone.transformations.ACCKernelsTrans.validate(), psyclone.transformations.ACCDataTrans.validate(), psyclone.psyir.transformations.psy_data_trans.PSyDataTrans.validate(), psyclone.psyir.transformations.region_trans.RegionTrans.validate(), and psyclone.transformations.ACCEnterDataTrans.validate().

Here is the call graph for this function:

◆ name()

def psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans.name (   self)
:returns: the name of the transformation as a string.
:rtype: str

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 169 of file nemo_arrayrange2loop_trans.py.

169  def name(self):
170  '''
171  :returns: the name of the transformation as a string.
172  :rtype: str
173 
174  '''
175  return type(self).__name__
176 
Here is the caller graph for this function:

◆ validate()

def psyclone.domain.nemo.transformations.nemo_arrayrange2loop_trans.NemoArrayRange2LoopTrans.validate (   self,
  node,
  options = None 
)
Perform various checks to ensure that it is valid to apply the
NemoArrayRange2LoopTrans transformation to the supplied PSyIR Node.

By default the validation will reject character arrays that PSyclone
understand as such, though this can be overriden by setting the
allow_string option to True. Note that PSyclone expresses syntax such
as `character(LEN=100)` as UnsupportedFortranType, and this
transformation will convert unknown or unsupported types to loops.

:param node: the node that is being checked.
:type node: :py:class:`psyclone.psyir.nodes.Range`
:param options: a dictionary with options for \
    transformations. No options are used in this \
    transformation. This is an optional argument that defaults \
    to None.
:type options: Optional[Dict[str, Any]]
:param bool options["allow_string"]: whether to allow the
    transformation on a character type array range. Defaults to False.

:raises TransformationError: if the node argument is not a \
    Range, if the Range node is not part of an ArrayReference, \
    if the Range node is not the outermost Range node of the \
    ArrayReference or if that ArrayReference does not \
    constitute the left hand side of an Assignment node.
:raises TransformationError: if the node argument has nested array \
    expressions with Ranges or is an invalid tree with ranges in \
    multiple locations of a structure of arrays.
:raises TransformationError: if the node argument contains a \
    non-elemental Operation or Call.
:raises TransformationError: if node contains a character type
                             child and the allow_strings option is
                             not set.

Reimplemented from psyclone.psyGen.Transformation.

Definition at line 177 of file nemo_arrayrange2loop_trans.py.

177  def validate(self, node, options=None):
178  '''Perform various checks to ensure that it is valid to apply the
179  NemoArrayRange2LoopTrans transformation to the supplied PSyIR Node.
180 
181  By default the validation will reject character arrays that PSyclone
182  understand as such, though this can be overriden by setting the
183  allow_string option to True. Note that PSyclone expresses syntax such
184  as `character(LEN=100)` as UnsupportedFortranType, and this
185  transformation will convert unknown or unsupported types to loops.
186 
187  :param node: the node that is being checked.
188  :type node: :py:class:`psyclone.psyir.nodes.Range`
189  :param options: a dictionary with options for \
190  transformations. No options are used in this \
191  transformation. This is an optional argument that defaults \
192  to None.
193  :type options: Optional[Dict[str, Any]]
194  :param bool options["allow_string"]: whether to allow the
195  transformation on a character type array range. Defaults to False.
196 
197  :raises TransformationError: if the node argument is not a \
198  Range, if the Range node is not part of an ArrayReference, \
199  if the Range node is not the outermost Range node of the \
200  ArrayReference or if that ArrayReference does not \
201  constitute the left hand side of an Assignment node.
202  :raises TransformationError: if the node argument has nested array \
203  expressions with Ranges or is an invalid tree with ranges in \
204  multiple locations of a structure of arrays.
205  :raises TransformationError: if the node argument contains a \
206  non-elemental Operation or Call.
207  :raises TransformationError: if node contains a character type
208  child and the allow_strings option is
209  not set.
210 
211  '''
212  # Am I Range node?
213  if not isinstance(node, Range):
214  raise TransformationError(
215  f"Error in NemoArrayRange2LoopTrans transformation. The "
216  f"supplied node argument should be a PSyIR Range, but "
217  f"found '{type(node).__name__}'.")
218  # Am I within an array reference?
219  if not node.parent or not isinstance(node.parent, ArrayMixin):
220  raise TransformationError(
221  f"Error in NemoArrayRange2LoopTrans transformation. The "
222  f"supplied node argument should be within an array access "
223  f"node, but found '{type(node.parent).__name__}'.")
224  # Is the array reference within an assignment?
225  assignment = node.ancestor(Assignment)
226  if not assignment:
227  raise TransformationError(
228  f"Error in NemoArrayRange2LoopTrans transformation. The "
229  f"supplied node argument should be within an Assignment node, "
230  f"but found a '{node}' that is not in an assignment.")
231  # Is the array reference the lhs of the assignment?
232  if node not in assignment.lhs.walk(Node):
233  raise TransformationError(
234  "Error in NemoArrayRange2LoopTrans transformation. The "
235  "supplied node argument should be within an array access "
236  "node that is within the left-hand-side of an Assignment "
237  "node, but it is on the right-hand-side.")
238 
239  # We don't support nested range expressions
240  for range_expr in assignment.walk(Range):
241  ancestor_array = range_expr.parent.ancestor(ArrayMixin)
242  if ancestor_array and any(index.walk(Range) for index
243  in ancestor_array.indices):
244  raise TransformationError(LazyString(
245  lambda: f"Error in NemoArrayRange2LoopTrans transformation"
246  f". This transformation does not support array assignments"
247  f" that contain nested Range structures, but found:"
248  f"\n{assignment.debug_string()}"))
249 
250  # Do a single walk to avoid doing a separate one for each type we need
251  nodes_to_check = assignment.walk((CodeBlock, Reference))
252 
253  # Does the rhs of the assignment have any operations/calls that are not
254  # elemental?
255  for cnode in assignment.rhs.walk(Call):
256  nodes_to_check.remove(cnode.routine)
257  if isinstance(cnode, IntrinsicCall):
258  if cnode.intrinsic.is_inquiry:
259  continue
260  name = cnode.intrinsic.name
261  type_txt = "IntrinsicCall"
262  else:
263  name = cnode.routine.name
264  type_txt = "Call"
265  if not cnode.is_elemental:
266  # pylint: disable=cell-var-from-loop
267  raise TransformationError(LazyString(
268  lambda: f"Error in NemoArrayRange2LoopTrans "
269  f"transformation. This transformation does not support non"
270  f"-elemental {type_txt}s on the rhs of the associated "
271  f"Assignment node, but found '{name}' in:\n"
272  f"{assignment.debug_string()}'."))
273 
274  # Do not allow to transform expressions with CodeBlocks
275  if any(isinstance(n, CodeBlock) for n in nodes_to_check):
276  raise TransformationError(LazyString(
277  lambda: f"Error in NemoArrayRange2LoopTrans transformation. "
278  f"This transformation does not support array assignments that"
279  f" contain a CodeBlock anywhere in the expression, but found:"
280  f"\n{assignment.debug_string()}"))
281 
282  references = [n for n in nodes_to_check if isinstance(n, Reference)]
283  for reference in references:
284  # As special case we always allow references to whole arrays as
285  # part of the LBOUND and UBOUND intrinsics, regardless of the
286  # restrictions below (e.g. is a UnresolvedType reference).
287  if isinstance(reference.parent, IntrinsicCall):
288  intrinsic = reference.parent.intrinsic
289  if intrinsic is IntrinsicCall.Intrinsic.LBOUND:
290  continue
291  if intrinsic is IntrinsicCall.Intrinsic.UBOUND:
292  continue
293 
294  # We allow any references that are part of a structure syntax - we
295  # analyse its child components by continuing the reference list
296  if isinstance(reference, (StructureReference, StructureMember)):
297  continue
298 
299  # We allow any references that have explicit array syntax
300  # because we infer that they are not scalars from the context
301  # where they are found (even if they have UnresolvedType)
302  if isinstance(reference, (ArrayReference, ArrayMember)):
303  continue
304 
305  # However, if it doesn't have array accessors or structure syntax,
306  # we must be sure that it represents a scalar.
307  if not isinstance(reference.symbol, DataSymbol) or \
308  not isinstance(reference.symbol.datatype, ScalarType):
309  raise TransformationError(
310  f"Error in NemoArrayRange2LoopTrans transformation. "
311  f"Variable '{reference.symbol.name}' must be a DataSymbol"
312  f" of ScalarType, but it's a '{reference.symbol}'.")
313 
314  # Is the Range node the outermost Range (as if not, the
315  # transformation would be invalid)?
316  for child in node.parent.indices[node.position+1:]:
317  if isinstance(child, Range):
318  raise TransformationError(
319  "Error in NemoArrayRange2LoopTrans transformation. This "
320  "transformation can only be applied to the outermost "
321  "Range.")
322 
323  if not options:
324  options = {}
325  allow_string_array = options.get("allow_string", False)
326  # If we allow string arrays then we can skip the check.
327  if not allow_string_array:
328  # ArrayMixin datatype lookup can fail if the indices contain a
329  # Call or Intrinsic Call. We catch this exception and continue
330  # for now - TODO #1799
331  for child in assignment.walk((Literal, Reference)):
332  try:
333  # Skip unresolved types
334  if (isinstance(child.datatype,
335  (UnresolvedType, UnsupportedType, NoType))
336  or (isinstance(child.datatype, ArrayType) and
337  isinstance(child.datatype.datatype,
338  (UnresolvedType, UnsupportedType)))):
339  continue
340  if (child.datatype.intrinsic ==
341  ScalarType.Intrinsic.CHARACTER):
342  raise TransformationError(
343  "The NemoArrayRange2LoopTrans transformation "
344  "doesn't allow character arrays by default. This "
345  "can be enabled by passing the allow_string "
346  "option to the transformation."
347  )
348  except NotImplementedError:
349  pass
350 
351 
352 # For automatic document generation
Here is the caller graph for this function:

The documentation for this class was generated from the following file: