예제 #1
0
def test_transform():
    '''Check that it is possible to create an instance of
    ArrayRange2LoopTrans and that it is a Transformation.

    '''
    assert ArrayRange2LoopTrans()
    assert isinstance(ArrayRange2LoopTrans(), Transformation)
예제 #2
0
def test_str():
    '''Test that the str of an instance of the ArrayRange2LoopTrans class
    returns the expected value.

    '''
    assert (str(ArrayRange2LoopTrans()) == "Convert a PSyIR assignment to an "
            "Array Range into a PSyIR Loop.")
예제 #3
0
def test_transform_apply_insert(tmpdir):
    '''Check that the PSyIR is transformed as expected when there are
    multiple statements in the PSyIR. The resultant Fortran code is used to
    confirm the transformation has worked correctly.

    '''
    trans = ArrayRange2LoopTrans()

    symbol_table = SymbolTable()
    symbol = DataSymbol("n", INTEGER_TYPE)
    symbol_table.add(symbol)
    # Create the first assignment. In Fortran notation: x(:) = y(n,:)
    lhs = create_array_x(symbol_table)
    rhs = create_array_y(symbol_table)
    assignment1 = Assignment.create(lhs, rhs)
    # Create the second assignment. In Fortran notation: y2(:,:) = z(:,n,:)
    lhs = create_array_y_2d_slice(symbol_table)
    rhs = create_array_z(symbol_table)
    assignment2 = Assignment.create(lhs, rhs)
    routine = KernelSchedule.create("work", symbol_table,
                                    [assignment1, assignment2])
    trans.apply(assignment1)
    trans.apply(assignment2)
    writer = FortranWriter()
    expected = ("  do idx = LBOUND(x, 1), UBOUND(x, 1), 1\n"
                "    x(idx)=y(n,idx)\n"
                "  enddo\n"
                "  do idx_1 = LBOUND(y2, 2), UBOUND(y2, 2), 1\n"
                "    y2(:,idx_1)=z(:,n,idx_1)\n"
                "  enddo\n")
    result = writer(routine)
    assert expected in result
    assert Compile(tmpdir).string_compiles(result)
예제 #4
0
def test_transform_multi_apply(tmpdir):
    '''Check that the ArrayRange2Loop transformation can be used to create
    nested loops by calling it multiple times when an array has
    multiple dimensions that use a range.

    '''
    trans = ArrayRange2LoopTrans()

    symbol_table = SymbolTable()
    symbol = DataSymbol("n", INTEGER_TYPE)
    symbol_table.add(symbol)
    lhs = create_array_y_2d_slice(symbol_table)
    rhs = create_array_z(symbol_table)
    assignment = Assignment.create(lhs, rhs)
    routine = KernelSchedule.create("work", symbol_table, [assignment])
    trans.apply(assignment)
    trans.apply(assignment)
    expected = ("  do idx = LBOUND(y2, 2), UBOUND(y2, 2), 1\n"
                "    do idx_1 = LBOUND(y2, 1), UBOUND(y2, 1), 1\n"
                "      y2(idx_1,idx)=z(idx_1,n,idx)\n"
                "    enddo\n"
                "  enddo\n")
    writer = FortranWriter()
    result = writer(routine)
    assert expected in result
    assert Compile(tmpdir).string_compiles(result)
예제 #5
0
def test_validate_intrinsic():
    '''Check that the validate method returns an exception if the rhs of
    the assignment contains an operator that only returns an array
    i.e. can't be performed elementwise. At the moment MATMUL is the
    only operator of this type.

    '''
    symbol_table = SymbolTable()
    array_x = create_array_x(symbol_table)
    array_y_2 = create_array_y_2d_slice(symbol_table)
    matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, array_y_2,
                                    array_x)
    reference = ArrayReference.create(
        symbol_table.lookup("x"), [create_range(symbol_table.lookup("x"), 1)])
    assignment = Assignment.create(reference, matmul)

    trans = ArrayRange2LoopTrans()
    with pytest.raises(TransformationError) as info:
        trans.validate(assignment)
    assert ("Error in ArrayRange2LoopTrans transformation. The rhs of the "
            "supplied Assignment node 'BinaryOperation[operator:'MATMUL']\n"
            "ArrayReference[name:'y2']\nRange[]\nRange[]\n\n"
            "ArrayReference[name:'x']\nRange[]\n' contains the "
            "MATMUL operator which can't be performed elementwise."
            in str(info.value))
예제 #6
0
def test_apply_calls_validate():
    '''Check that the apply() method calls the validate method.'''
    trans = ArrayRange2LoopTrans()
    with pytest.raises(TransformationError) as info:
        trans.apply(None)
    assert ("Error in ArrayRange2LoopTrans transformation. The supplied node "
            "argument should be a PSyIR Assignment, but found 'NoneType'."
            in str(info.value))
예제 #7
0
def test_validate():
    '''Test that the validate method in the ArrayRange2LoopTrans class
    raises the expected exceptions.

    '''
    trans = ArrayRange2LoopTrans()
    with pytest.raises(TransformationError) as info:
        trans.validate(Node())
    assert (
        "Error in ArrayRange2LoopTrans transformation. The supplied node "
        "argument should be a PSyIR Assignment, but found 'Node'."
        in str(info.value))

    with pytest.raises(TransformationError) as info:
        trans.validate(Assignment.create(DataNode(), DataNode()))
    assert (
        "Error in ArrayRange2LoopTrans transformation. The lhs of the "
        "supplied Assignment node should be a PSyIR ArrayReference, but found "
        "'DataNode'." in str(info.value))

    array_symbol = DataSymbol("x", ArrayType(INTEGER_TYPE, [10, 10]))
    one = Literal("1", INTEGER_TYPE)
    array_assignment = ArrayReference.create(array_symbol, [one, one.copy()])
    with pytest.raises(TransformationError) as info:
        trans.validate(Assignment.create(array_assignment, DataNode()))
    assert (
        "Error in ArrayRange2LoopTrans transformation. The lhs of the "
        "supplied Assignment node should be a PSyIR ArrayReference with at "
        "least one "
        "of its dimensions being a Range, but found None in "
        "'ArrayReference[name:'x']\\nLiteral[value:'1', "
        "Scalar<INTEGER, UNDEFINED>]\\nLiteral[value:'1', Scalar<INTEGER, "
        "UNDEFINED>]\\n'." in str(info.value))

    array_x = create_array_x(SymbolTable())
    assignment = Assignment.create(
        create_array_x(SymbolTable()), array_x)
    trans.validate(assignment)

    array_x.children[0].step = Literal("2", INTEGER_TYPE)
    with pytest.raises(TransformationError) as info:
        trans.validate(assignment)
    assert (
        "The ArrayRange2LoopTrans transformation only supports ranges that "
        "are known to be the same as each other but array access 'x' "
        "dimension 0 and 'x' dimension 0 are either different or can't be "
        "determined in the assignment 'Assignment[]\\n"
        "ArrayReference[name:'x']\\nRange[]\\n"
        "ArrayReference[name:'x']\\nRange[]\\n'."
        in str(info.value))
예제 #8
0
def test_transform_apply(lhs_create, rhs_create, expected, tmpdir):
    '''Check that the PSyIR is transformed as expected for various types
    of ranges in an array. The resultant Fortran code is used to
    confirm the transformation has worked correctly.

    '''
    trans = ArrayRange2LoopTrans()
    symbol_table = SymbolTable()
    symbol = DataSymbol("n", INTEGER_TYPE)
    symbol_table.add(symbol)
    lhs = lhs_create(symbol_table)
    rhs = rhs_create(symbol_table)
    assignment = Assignment.create(lhs, rhs)
    routine = KernelSchedule.create("work", symbol_table, [assignment])
    trans.apply(assignment)
    writer = FortranWriter()
    result = writer(routine)
    assert expected in result
    assert Compile(tmpdir).string_compiles(result)
예제 #9
0
def test_string_compare():
    '''Check that the string_compare utility function in
    ArrayRange2LoopTrans works as expected.

    '''
    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.string_compare(None, None)
    assert ("The first argument to the string_compare method should be a Node "
            "but found 'NoneType'." in str(info.value))

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.string_compare(Node(), None)
    assert (
        "The second argument to the string_compare method should be a Node "
        "but found 'NoneType'." in str(info.value))

    node1 = Literal("1.0", REAL_TYPE)
    node2 = BinaryOperation.create(BinaryOperation.Operator.MUL, node1, node1)
    node3 = BinaryOperation.create(BinaryOperation.Operator.MAX, node2, node2)
    assert ArrayRange2LoopTrans.string_compare(node3, node3) is True
    assert ArrayRange2LoopTrans.string_compare(node3, node2) is False
예제 #10
0
def test_name():
    '''Check that the name property of the ArrayRange2LoopTrans class
    returns the expected value.

    '''
    assert ArrayRange2LoopTrans().name == "ArrayRange2LoopTrans"
예제 #11
0
def test_same_range():
    '''Test that the same_range utility function behaves in the expected
    way.

    '''
    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.same_range(None, None, None, None)
    assert ("The first argument to the same_range() method should be an "
            "Array but found 'NoneType'." in str(info.value))

    array_type = ArrayType(REAL_TYPE, [10])
    array_value = Array.create(DataSymbol("dummy", array_type),
                               children=[DataNode("x")])
    array_range = Array.create(DataSymbol("dummy", array_type),
                               children=[Range()])

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.same_range(array_value, None, None, None)
    assert ("The second argument to the same_range() method should be an "
            "int but found 'NoneType'." in str(info.value))

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.same_range(array_value, 1, None, None)
    assert ("The third argument to the same_range() method should be an "
            "Array but found 'NoneType'." in str(info.value))

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.same_range(array_value, 1, array_value, None)
    assert ("The fourth argument to the same_range() method should be an "
            "int but found 'NoneType'." in str(info.value))

    with pytest.raises(IndexError) as info:
        ArrayRange2LoopTrans.same_range(array_value, 1, array_value, 2)
    assert ("The value of the second argument to the same_range() method "
            "'1' should be less than the number of dimensions '1' in the "
            "associated array 'array1'." in str(info.value))

    with pytest.raises(IndexError) as info:
        ArrayRange2LoopTrans.same_range(array_value, 0, array_value, 2)
    assert ("The value of the fourth argument to the same_range() method "
            "'2' should be less than the number of dimensions '1' in the "
            "associated array 'array2'." in str(info.value))

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.same_range(array_value, 0, array_value, 0)
    assert ("The child of the first array argument at the specified index (0) "
            "should be a Range node, but found 'DataNode'" in str(info.value))

    with pytest.raises(TypeError) as info:
        ArrayRange2LoopTrans.same_range(array_range, 0, array_value, 0)
    assert ("The child of the second array argument at the specified index "
            "(0) should be a Range node, but found 'DataNode'"
            in str(info.value))

    # lower bounds both use lbound, upper bounds both use ubound and
    # step is the same so everything matches.
    array_x = create_array_x(SymbolTable())
    array_x_2 = create_array_x(SymbolTable())
    assert ArrayRange2LoopTrans.same_range(array_x, 0, array_x_2, 0) is True

    # steps are different (calls string_compare)
    tmp = array_x_2.children[0].step
    array_x_2.children[0].step = Literal("2", INTEGER_TYPE)
    assert ArrayRange2LoopTrans.same_range(array_x, 0, array_x_2, 0) is False

    # Put step value back to what it was in case it affects the ubound
    # and lbound tests
    array_x_2.children[0].step = tmp

    # one of upper bounds uses ubound, other does not
    tmp1 = array_x_2.children[0].stop
    array_x_2.children[0].stop = Literal("2", INTEGER_TYPE)
    assert ArrayRange2LoopTrans.same_range(array_x, 0, array_x_2, 0) is False

    # neither use upper bound and are different (calls string_compare)
    tmp2 = array_x.children[0].stop
    array_x.children[0].stop = Literal("1", INTEGER_TYPE)
    assert ArrayRange2LoopTrans.same_range(array_x, 0, array_x_2, 0) is False

    # Put upper bounds back to what they were in case they affect the
    # lbound tests
    array_x_2.children[0].stop = tmp1
    array_x.children[0].stop = tmp2

    # one of lower bounds uses lbound, other does not
    array_x_2.children[0].start = Literal("1", INTEGER_TYPE)
    assert ArrayRange2LoopTrans.same_range(array_x, 0, array_x_2, 0) is False

    # neither use lower bound and are different (calls string_compare)
    array_x.children[0].start = Literal("2", INTEGER_TYPE)
    assert ArrayRange2LoopTrans.same_range(array_x, 0, array_x_2, 0) is False