def test_transform(): '''Check that it is possible to create an instance of NemoArrayRange2LoopTrans and that it is a Transformation. ''' assert NemoArrayRange2LoopTrans() assert isinstance(NemoArrayRange2LoopTrans(), Transformation)
def test_apply_reference_literal(): '''Check that the apply method add bounds appropriately when the config file specifies a lower bound as a reference and an upper bound as a literal. ''' _, invoke_info = get_invoke("implicit_many_dims.f90", api=API, idx=0) # Create a new config instance and load a test config file with # the bounds information set the way we want. config = Config.get(do_not_load_file=True) config.load(config_file=TEST_CONFIG) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() for index in range(4, -1, -1): range_node = array_ref.children[index] trans.apply(range_node) # Remove this config file so the next time the default one will be # loaded (in case we affect other tests) Config._instance = None writer = FortranWriter() result = writer(schedule) assert ("do idx = LBOUND(umask, 5), UBOUND(umask, 5), 1\n" " do jt = 1, UBOUND(umask, 4), 1\n" " do jk = jpk, 1, 1\n" " do jj = 1, jpj, 1\n" " do ji = jpi, 1, 1\n" " umask(ji,jj,jk,jt,idx) = vmask(ji,jj,jk,jt,idx) + 1.0\n" " enddo\n" " enddo\n" " enddo\n" " enddo\n" "enddo" in result)
def test_str(): '''Test that the str of an instance of the NemoArrayRange2LoopTrans class returns the expected value. ''' assert (str(NemoArrayRange2LoopTrans()) == "Convert the PSyIR assignment " "for a specified ArrayReference Range into a PSyIR NemoLoop.")
def test_apply_existing_names(): '''Check that the apply method uses existing iterators appropriately when their symbols are already defined. ''' _, invoke_info = get_invoke("implicit_do.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() symbol_table = schedule.symbol_table symbol_table.add(DataSymbol("ji", INTEGER_TYPE)) symbol_table.add(DataSymbol("jj", INTEGER_TYPE)) symbol_table.add(DataSymbol("jk", INTEGER_TYPE)) trans.apply(array_ref.children[2]) trans.apply(array_ref.children[1]) trans.apply(array_ref.children[0]) writer = FortranWriter() result = writer(schedule) assert ("do jk = 1, jpk, 1\n" " do jj = 1, jpj, 1\n" " do ji = 1, jpi, 1\n" " umask(ji,jj,jk) = 0.0e0\n" " enddo\n" " enddo\n" "enddo" in result)
def test_apply_calls_validate(): '''Check that the apply() method calls the validate method.''' trans = NemoArrayRange2LoopTrans() with pytest.raises(TransformationError) as info: trans.apply(None) assert ("Error in NemoArrayRange2LoopTrans transformation. The supplied " "node argument should be a PSyIR Range, but found 'NoneType'." in str(info.value))
def test_valid_node(): '''Check that the validate() method raises the expected exception if the supplied node is invalid. ''' trans = NemoArrayRange2LoopTrans() with pytest.raises(TransformationError) as info: trans.validate(None) assert ("Error in NemoArrayRange2LoopTrans transformation. The supplied " "node argument should be a PSyIR Range, but found 'NoneType'." in str(info.value))
def test_apply_different_num_dims(): '''Check that the apply method raises an exception when the number of range dimensions differ in different arrays. This should never happen as it is invalid PSyIR. ''' _, invoke_info = get_invoke("implicit_mismatch_error.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() with pytest.raises(InternalError) as info: trans.apply(array_ref.children[1]) assert ("The number of ranges in the arrays within this " "assignment are not equal. This is invalid PSyIR and " "should never happen." in str(info.value))
def test_not_outermost_range(): '''Check that the validate() method raises the expected exception if the supplied node is not the outermost Range within an array reference. ''' _, invoke_info = get_invoke("implicit_do2.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() my_range = array_ref.children[0] with pytest.raises(TransformationError) as info: trans.validate(my_range) assert ("Error in NemoArrayRange2LoopTrans transformation. This " "transformation can only be applied to the outermost " "Range." in str(info.value))
def test_array_valued_operator(): '''Check that the vaidate() method raises the expected exception if an array valued operation is found on the rhs of the assignment node. ''' _, invoke_info = get_invoke("array_valued_operation.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() with pytest.raises(TransformationError) as info: trans.apply(array_ref.children[0]) assert ( "Error in NemoArrayRange2LoopTrans transformation. This " "transformation does not support array valued operations on the rhs " "of the associated Assignment node, but found 'MATMUL'." in str(info.value))
def test_within_lhs_assignment(): '''Check that the validate() method raises the expected exception if the supplied node is not within an array reference that is within the lhs of an assignment (i.e. it is within the rhs). ''' _, invoke_info = get_invoke("implicit_do2.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.rhs trans = NemoArrayRange2LoopTrans() my_range = array_ref.children[0] with pytest.raises(TransformationError) as info: trans.validate(my_range) assert ("Error in NemoArrayRange2LoopTrans transformation. The " "supplied node argument should be within an ArrayReference " "node that is within the left-hand-side of an Assignment " "node, but it is on the right-hand-side." in str(info.value))
def test_apply_array_valued_function(): '''Check that the apply method does not modify range nodes when they are used to specify the part of an array to pass into an array valued function. ''' _, invoke_info = get_invoke("array_valued_function.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[1] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() trans.apply(array_ref.children[2]) writer = FortranWriter() result = writer(schedule) assert ("jn = 2\n" "do jk = 1, jpk, 1\n" " z3d(1,:,jk) = ptr_sjk(pvtr(:,:,:),btmsk(:,:,jn) * btm30(:,:))\n" "enddo" in result)
def test_loop_variable_name_error(datatype): '''Check that the expected exception is raised when the config file specifies a loop iteration name but it is already declared in the code as something that is not a scalar. ''' _, invoke_info = get_invoke("implicit_do.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() symbol_table = schedule.symbol_table symbol_table.add(DataSymbol("jk", datatype)) with pytest.raises(TransformationError) as info: trans.apply(array_ref.children[2]) assert ("The config file specifies 'jk' as the name of the iteration " "variable but this is already declared in the code as something " "that is not a scalar integer, or is a deferred type." in str(info.value))
def test_within_array_reference(): '''Check that the validate() method raises the expected exception if the supplied node is not within an array reference. ''' _, invoke_info = get_invoke("implicit_do.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() my_range = array_ref.children[2] for parent, result in [(assignment, "Assignment"), (None, "NoneType")]: my_range._parent = parent with pytest.raises(TransformationError) as info: trans.validate(my_range) assert ("Error in NemoArrayRange2LoopTrans transformation. The " "supplied node argument should be within an " "ArrayReference node, but found '{0}'.".format(result) in str(info.value))
def test_apply_var_name(): '''Check that the variable name that is used when no names are specified in the config file does not clash with an existing symbol. ''' _, invoke_info = get_invoke("implicit_many_dims.f90", api=API, idx=0) schedule = invoke_info.schedule symbol_table = schedule.symbol_table symbol_table.add(DataSymbol("idx", INTEGER_TYPE)) assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() range_node = array_ref.children[4] trans.apply(range_node) writer = FortranWriter() result = writer(schedule) assert ("do idx_1 = LBOUND(umask, 5), UBOUND(umask, 5), 1\n" " umask(:,:,:,:,idx_1) = vmask(:,:,:,:,idx_1) + 1.0\n" "enddo" in result)
def apply(self, node, options=None): '''Apply the NemoOuterArrayRange2Loop transformation to the specified node if the node is an Assignment and the left-hand-side of the assignment is an Array Reference containing at least one Range node specifying an access to an array index. If this is the case then the outermost Range nodes within array references within the assignment are replaced with references to a loop index. A NemoLoop loop (with the same loop index) is also placed around the modified assignment statement. If the array reference on the left-hand-side of the assignment only had one range node as an index (so now has none) then the assigment is also placed within a NemoKern. The name of the loop index is taken from the PSyclone configuration file if a name exists for the particular array index, otherwise a new name is generated. The bounds of the loop are taken from the Range node if they are provided. If not, the loop bounds are taken from the PSyclone configuration file if bounds values are supplied. If not, the LBOUND or UBOUND intrinsics are used as appropriate. The type of the NemoLoop is also taken from the configuration file if it is supplied for that index, otherwise it is specified as being "unknown". :param node: an Assignment node. :type node: :py:class:`psyclone.psyir.nodes.Assignment` :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: dict of string:values or None ''' self.validate(node) # get lhs array lhs_array_ref = node.lhs index = get_outer_index(lhs_array_ref) nemo_arrayrange2loop = NemoArrayRange2LoopTrans() nemo_arrayrange2loop.apply(lhs_array_ref.children[index])
def test_apply_fixed_bounds(): '''Check that the apply method uses bounds information from the range node if it is supplied. ''' _, invoke_info = get_invoke("implicit_do_slice.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() for index in range(2, -1, -1): range_node = array_ref.children[index] trans.apply(range_node) writer = FortranWriter() result = writer(schedule) assert ("do jk = 1, jpk, 1\n" " do jj = 2, 4, 1\n" " do ji = 1, jpi, 1\n" " umask(ji,jj,jk) = 0.0e0\n" " enddo\n" " enddo\n" "enddo" in result)
def test_apply_different_dims(): '''Check that the apply method adds loop iterators appropriately when the range dimensions differ in different arrays. ''' _, invoke_info = get_invoke("implicit_different_dims.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() for index in [4, 2, 0]: range_node = array_ref.children[index] trans.apply(range_node) writer = FortranWriter() result = writer(schedule) assert ( "do idx = LBOUND(umask, 5), UBOUND(umask, 5), 1\n" " do jk = 1, jpk, 1\n" " do ji = 1, jpi, 1\n" " umask(ji,jpj,jk,ndim,idx) = vmask(jpi,ji,jk,idx,ndim) + 1.0\n" " enddo\n" " enddo\n" "enddo" in result)
def test_apply_bounds(): '''Check that the apply method uses a) configuration bounds if they are provided or b) lbound and ubound intrinsics when no bounds information is available. Also check that a NemoKern is added between the assignment and the innermost enclosing loop after the last range has been transformed into an explicit loop. ''' _, invoke_info = get_invoke("implicit_many_dims.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() for index in range(4, -1, -1): assert not schedule.walk(NemoKern) range_node = array_ref.children[index] trans.apply(range_node) assert schedule.walk(NemoKern) assert isinstance(assignment.parent, Schedule) assert isinstance(assignment.parent.parent, NemoKern) assert isinstance(assignment.parent.parent.parent, Schedule) assert isinstance(assignment.parent.parent.parent.parent, NemoLoop) assert assignment.parent.parent.parent.parent.loop_type == "lon" writer = FortranWriter() result = writer(schedule) assert ( "do idx = LBOUND(umask, 5), UBOUND(umask, 5), 1\n" " do jt = 1, UBOUND(umask, 4), 1\n" " do jk = 1, jpk, 1\n" " do jj = 1, jpj, 1\n" " do ji = 1, jpi, 1\n" " umask(ji,jj,jk,jt,idx) = vmask(ji,jj,jk,jt,idx) + 1.0\n" " enddo\n" " enddo\n" " enddo\n" " enddo\n" "enddo" in result)
def test_name(): '''Check that the name property of the ArrayRange2LoopTrans class returns the expected value. ''' assert NemoArrayRange2LoopTrans().name == "NemoArrayRange2LoopTrans"