Example #1
0
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)
Example #2
0
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))
Example #3
0
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
Example #4
0
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])
Example #5
0
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])
Example #6
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))
Example #7
0
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"
Example #8
0
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))
Example #9
0
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)