def test_script_trans(): ''' Checks that generator.py works correctly when a transformation is provided as a script, i.e. it applies the transformations correctly. We use loop fusion as an example. ''' root_path = os.path.dirname(os.path.abspath(__file__)) base_path = os.path.join(root_path, "test_files", "dynamo0p3") # First loop fuse explicitly (without using generator.py) parse_file = os.path.join(base_path, "4_multikernel_invokes.f90") _, invoke_info = parse(parse_file, api="dynamo0.3") psy = PSyFactory("dynamo0.3", distributed_memory=True).create(invoke_info) invoke = psy.invokes.get("invoke_0") schedule = invoke.schedule loop1 = schedule.children[4] loop2 = schedule.children[5] trans = LoopFuseTrans() trans.apply(loop1, loop2) generated_code_1 = psy.gen # Second loop fuse using generator.py and a script _, generated_code_2 = generate(parse_file, api="dynamo0.3", script_name=os.path.join( base_path, "loop_fuse_trans.py")) # remove module so we do not affect any following tests delete_module("loop_fuse_trans") # third - check that the results are the same ... assert str(generated_code_1) == str(generated_code_2)
def test_fusetrans_error_not_same_parent(): ''' Check that we reject attempts to fuse loops which don't share the same parent ''' loop1 = Loop.create(DataSymbol("i", INTEGER_TYPE), Literal("1", INTEGER_TYPE), Literal("10", INTEGER_TYPE), Literal("1", INTEGER_TYPE), [Return()]) sch1 = Schedule() sch1.addchild(loop1) sch2 = Schedule() loop2 = Loop.create(DataSymbol("j", INTEGER_TYPE), Literal("1", INTEGER_TYPE), Literal("10", INTEGER_TYPE), Literal("1", INTEGER_TYPE), [Return()]) sch2.addchild(loop2) fuse = LoopFuseTrans() # Try to fuse loops with different parents with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. Loops do not have the " "same parent" in str(err.value))
def trans(psy): ''' A test loop fusion transformation for use with the transformation unit tests ''' invoke = psy.invokes.get("invoke_0") schedule = invoke.schedule loop1 = schedule.children[4] loop2 = schedule.children[5] transform = LoopFuseTrans() transform.apply(loop1, loop2) return psy
def test_loop_fuse_non_adjacent_nodes(): ''' Test that an appropriate error is raised when we attempt to fuse two loops that are not adjacent to one another in the schedule ''' _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0") schedule = invoke.schedule lftrans = LoopFuseTrans() # Attempt to fuse two loops that are not adjacent to one another # in the schedule with pytest.raises(TransformationError): _, _ = lftrans.apply(schedule.children[0], schedule.children[2])
def test_loop_fuse_on_non_siblings(): ''' Test that an appropriate error is raised when we attempt to fuse two loops that do not share the same parent node in the schedule ''' _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0") schedule = invoke.schedule lftrans = LoopFuseTrans() # Attempt to fuse an outer loop with an inner loop with pytest.raises(TransformationError): _, _ = lftrans.apply(schedule.children[0], schedule.children[1].children[0])
def apply(self, node1, node2, options=None): ''' Fuses two `psyclone.gocean1p0.GOLoop` loops after performing validity checks by calling :py:meth:`LoopFuseTrans.apply` method of the base class. :param node1: the first Node representing a GOLoop. :type node1: :py:class:`psyclone.gocean1p0.GOLoop` :param node2: the second Node representing a GOLoop. :type node2: :py:class:`psyclone.gocean1p0.GOLoop` :param options: a dictionary with options for transformations. :type options: dictionary of string:values or None :returns: two-tuple of the modified Schedule and a record of \ the transformation. :rtype: (:py:class:`psyclone.psyir.nodes.Schedule`, \ :py:class:`psyclone.undoredo.Memento`) :raises TransformationError: if the supplied loops are over \ different grid-point types. :raises TransformationError: if there is an unexpected exception. ''' # Validate first self.validate(node1, node2, options=options) # Now check for GOcean-specific constraints before applying # the transformation try: return LoopFuseTrans.apply(self, node1, node2, options) except Exception as err: raise TransformationError( "Error in {0} transformation. Unexpected exception: {1}". format(self.name, err))
def test_loop_trans_name(): ''' Check that the name method works as expected. ''' # We have to use sub-classes of LoopTrans as it itself is abstract. trans1 = OMPParallelLoopTrans() assert trans1.name == "OMPParallelLoopTrans" trans2 = LoopFuseTrans() assert trans2.name == "LoopFuseTrans"
def test_loop_fuse_with_not_a_loop(): ''' Test that an appropriate error is raised by the LoopFuseTrans base class wen we attempt to fuse a loop with something that is not a loop ''' _, invoke = get_invoke("openmp_fuse_test.f90", API, name="invoke_0") schedule = invoke.schedule # Use the bare LoopFuseTrans in order tests its error checking lftrans = LoopFuseTrans() ompf = GOceanOMPParallelLoopTrans() # Enclose the first loop within an OMP parallel do ompf.apply(schedule.children[0]) # Attempt to (erroneously) fuse this OMP parallel do # with the next loop in the schedule with pytest.raises(TransformationError) as ex: lftrans.apply(schedule.children[0], schedule.children[1]) # Exercise the __str__ method of TransformationError assert ("Target of LoopFuseTrans transformation must be a sub-class of " "Loop but got 'OMPParallelDoDirective'" in str(ex.value))
def test_fusetrans_error_incomplete(): ''' Check that we reject attempts to fuse loops which are incomplete. ''' sch = Schedule() loop1 = Loop(variable=DataSymbol("i", INTEGER_TYPE)) loop2 = Loop(variable=DataSymbol("j", INTEGER_TYPE)) sch.addchild(loop1) sch.addchild(loop2) fuse = LoopFuseTrans() # Check first loop with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. The target loop must have " "four children but found: []" in str(err.value)) loop1.addchild(Literal("start", INTEGER_TYPE)) loop1.addchild(Literal("stop", INTEGER_TYPE)) loop1.addchild(Literal("step", INTEGER_TYPE)) loop1.addchild(Schedule()) loop1.loop_body.addchild(Return()) # Check second loop with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. The target loop must have " "four children but found: []" in str(err.value)) loop2.addchild(Literal("start", INTEGER_TYPE)) loop2.addchild(Literal("stop", INTEGER_TYPE)) loop2.addchild(Literal("step", INTEGER_TYPE)) loop2.addchild(Schedule()) loop2.loop_body.addchild(Return()) # Validation should now pass fuse.validate(loop1, loop2)