示例#1
0
def test_reference_accesses_bounds(operator_type):
    '''Test that the reference_accesses method behaves as expected when
    the reference is the first argument to either the lbound or ubound
    intrinsic as that is simply looking up the array bounds (therefore
    var_access_info should be empty) and when the reference is the
    second argument of either the lbound or ubound intrinsic (in which
    case the access should be a read).

    '''
    # Note, one would usually expect UBOUND to provide the upper bound
    # of a range but to simplify the test both LBOUND and UBOUND are
    # used for the lower bound. This does not affect the test.
    one = Literal("1", INTEGER_TYPE)
    array_symbol = DataSymbol("test", ArrayType(REAL_TYPE, [10]))
    array_ref1 = Reference(array_symbol)
    array_ref2 = Reference(array_symbol)
    array_access = ArrayReference.create(array_symbol, [one])

    # test when first or second argument to LBOUND or UBOUND is an
    # array reference
    operator = BinaryOperation.create(operator_type, array_ref1, array_ref2)
    array_access.children[0] = Range.create(operator, one.copy(), one.copy())
    var_access_info = VariablesAccessInfo()
    array_ref1.reference_accesses(var_access_info)
    assert str(var_access_info) == ""
    var_access_info = VariablesAccessInfo()
    array_ref2.reference_accesses(var_access_info)
    assert str(var_access_info) == "test: READ"
示例#2
0
def test_is_not_array_range():
    ''' Test that is_array_range correctly rejects things that aren't
    an assignment to an array range.

    '''
    int_one = Literal("1", INTEGER_SINGLE_TYPE)
    one = Literal("1.0", REAL_TYPE)
    var = DataSymbol("x", REAL_TYPE)
    reference = Reference(var)

    # lhs is not an array
    assignment = Assignment.create(reference, one)
    assert assignment.is_array_range is False

    # lhs is an array reference but has no range
    array_type = ArrayType(REAL_TYPE, [10, 10])
    symbol = DataSymbol("y", array_type)
    array_ref = Reference(symbol)
    assignment = Assignment.create(array_ref, one.copy())
    assert assignment.is_array_range is False

    # lhs is an array reference but the single index value is obtained
    # using an array range, y(1, SUM(map(:), 1)) = 1.0
    int_array_type = ArrayType(INTEGER_SINGLE_TYPE, [10])
    map_sym = DataSymbol("map", int_array_type)
    start = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                   Reference(map_sym), int_one.copy())
    stop = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                  Reference(map_sym), int_one.copy())
    my_range = Range.create(start, stop)
    sum_op = BinaryOperation.create(BinaryOperation.Operator.SUM,
                                    ArrayReference.create(map_sym, [my_range]),
                                    int_one.copy())
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), sum_op]), one.copy())
    assert assignment.is_array_range is False

    # When the slice has two operator ancestors, one of which is a reduction
    # e.g y(1, SUM(ABS(map(:)), 1)) = 1.0
    abs_op = UnaryOperation.create(
        UnaryOperation.Operator.ABS,
        ArrayReference.create(map_sym, [my_range.copy()]))
    sum_op2 = BinaryOperation.create(BinaryOperation.Operator.SUM, abs_op,
                                     int_one.copy())
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), sum_op2]), one.copy())
    assert assignment.is_array_range is False

    # lhs is a scalar member of a structure
    grid_type = StructureType.create([
        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC),
        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC)
    ])
    grid_type_symbol = DataTypeSymbol("grid_type", grid_type)
    grid_sym = DataSymbol("grid", grid_type_symbol)
    assignment = Assignment.create(StructureReference.create(grid_sym, ["dx"]),
                                   one.copy())
    assert assignment.is_array_range is False
示例#3
0
def test_range_create():
    ''' Check that the Range.create() method behaves as intended. '''
    start = Literal("1", INTEGER_SINGLE_TYPE)
    stop = Literal("10", INTEGER_SINGLE_TYPE)
    # No step
    erange = Range.create(start, stop)
    assert erange.children[0] is start
    assert erange.children[1] is stop
    assert erange.children[2].value == "1"
    # Step supplied
    erange3 = Range.create(start.copy(), stop.copy(),
                           Literal("5", INTEGER_SINGLE_TYPE),)
    assert erange3.children[2].value == "5"
示例#4
0
def test_array_range_with_reduction():
    ''' Test that we correctly identify an array range when it is the result
        of a reduction from an array, e.g x(1, INT(SUM(map(:, :), 1))) = 1.0

    '''
    one = Literal("1.0", REAL_TYPE)
    int_one = Literal("1", INTEGER_TYPE)
    int_two = Literal("2", INTEGER_TYPE)
    int_array_type = ArrayType(INTEGER_SINGLE_TYPE, [10, 10])
    map_sym = DataSymbol("map", int_array_type)
    array_type = ArrayType(REAL_TYPE, [10, 10])
    symbol = DataSymbol("x", array_type)
    lbound1 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(map_sym), int_one.copy())
    ubound1 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(map_sym), int_one.copy())
    my_range1 = Range.create(lbound1, ubound1)
    lbound2 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(map_sym), int_two.copy())
    ubound2 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(map_sym), int_two.copy())
    my_range2 = Range.create(lbound2, ubound2)
    bsum_op = BinaryOperation.create(
        BinaryOperation.Operator.SUM,
        ArrayReference.create(map_sym, [my_range1, my_range2]), int_one.copy())
    int_op2 = UnaryOperation.create(UnaryOperation.Operator.INT, bsum_op)
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), int_op2]), one.copy())
    assert assignment.is_array_range is True
示例#5
0
def test_create_struct_reference():
    ''' Tests for the _create_struct_reference() utility. '''
    one = Literal("1", INTEGER_TYPE)
    with pytest.raises(InternalError) as err:
        _create_struct_reference(None, StructureReference, Symbol("fake"),
                                 ["hello", 1], [])
    assert ("List of members must contain only strings or tuples but found "
            "entry of type 'int'" in str(err.value))
    with pytest.raises(NotImplementedError) as err:
        _create_struct_reference(None, StructureType, Symbol("fake"),
                                 ["hello"], [])
    assert "Cannot create structure reference for type '" in str(err.value)
    with pytest.raises(InternalError) as err:
        _create_struct_reference(None, StructureReference,
                                 DataSymbol("fake", DeferredType()), ["hello"],
                                 [one.copy()])
    assert ("Creating a StructureReference but array indices have been "
            "supplied" in str(err.value))
    with pytest.raises(InternalError) as err:
        _create_struct_reference(None, ArrayOfStructuresReference,
                                 DataSymbol("fake", DeferredType()), ["hello"],
                                 [])
    assert ("Cannot create an ArrayOfStructuresReference without one or more "
            "index expressions" in str(err.value))
    ref = _create_struct_reference(None, StructureReference,
                                   DataSymbol("fake", DeferredType()),
                                   ["hello"], [])
    assert isinstance(ref, StructureReference)
    assert isinstance(ref.member, Member)
    assert ref.member.name == "hello"
    # Check that we can create an ArrayOfStructuresReference and that any
    # PSyIR nodes are copied.
    idx_var = one.copy()
    idx_var2 = one.copy()
    aref = _create_struct_reference(None, ArrayOfStructuresReference,
                                    DataSymbol("fake", DeferredType()),
                                    ["a", ("b", [idx_var2])], [idx_var])
    assert isinstance(aref, ArrayOfStructuresReference)
    assert isinstance(aref.member, StructureMember)
    assert aref.member.name == "a"
    assert aref.member.member.name == "b"
    assert len(aref.member.member.indices) == 1
    assert aref.member.member.indices[0] is not idx_var2
    assert len(aref.indices) == 1
    assert aref.indices[0] is not idx_var
示例#6
0
def create_matmul():
    '''Utility function that creates a valid matmul node for use with
    subsequent tests.

    '''
    symbol_table = SymbolTable()
    one = Literal("1", INTEGER_TYPE)
    two = Literal("2", INTEGER_TYPE)
    index = DataSymbol("idx", INTEGER_TYPE, constant_value=3)
    symbol_table.add(index)
    array_type = ArrayType(REAL_TYPE, [5, 10, 15])
    mat_symbol = DataSymbol("x", array_type)
    symbol_table.add(mat_symbol)
    lbound1 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(mat_symbol), one.copy())
    ubound1 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(mat_symbol), one.copy())
    my_mat_range1 = Range.create(lbound1, ubound1, one.copy())
    lbound2 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(mat_symbol), two.copy())
    ubound2 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(mat_symbol), two.copy())
    my_mat_range2 = Range.create(lbound2, ubound2, one.copy())
    matrix = ArrayReference.create(
        mat_symbol, [my_mat_range1, my_mat_range2,
                     Reference(index)])
    array_type = ArrayType(REAL_TYPE, [10, 20])
    vec_symbol = DataSymbol("y", array_type)
    symbol_table.add(vec_symbol)
    lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                    Reference(vec_symbol), one.copy())
    ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                    Reference(vec_symbol), one.copy())
    my_vec_range = Range.create(lbound, ubound, one.copy())
    vector = ArrayReference.create(
        vec_symbol, [my_vec_range, Reference(index)])
    matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, matrix,
                                    vector)
    lhs_type = ArrayType(REAL_TYPE, [10])
    lhs_symbol = DataSymbol("result", lhs_type)
    symbol_table.add(lhs_symbol)
    lhs = Reference(lhs_symbol)
    assign = Assignment.create(lhs, matmul)
    KernelSchedule.create("my_kern", symbol_table, [assign])
    return matmul
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_apply4(tmpdir):
    '''Test that the matmul2code apply method produces the expected
    PSyIR. We use the Fortran backend to help provide the test for
    correctness. This example make the lhs be the same array as the
    second operand of the matmul (the vector in this case).

    '''
    trans = Matmul2CodeTrans()
    one = Literal("1", INTEGER_TYPE)
    five = Literal("5", INTEGER_TYPE)
    matmul = create_matmul()
    root = matmul.root
    assignment = matmul.parent
    vector = assignment.scope.symbol_table.lookup("y")
    assignment.children[0] = ArrayReference.create(
        vector, [Range.create(one, five, one.copy()),
                 one.copy()])
    trans.apply(matmul)
    writer = FortranWriter()
    result = writer(root)
    assert ("subroutine my_kern()\n"
            "  integer, parameter :: idx = 3\n"
            "  real, dimension(5,10,15) :: x\n"
            "  real, dimension(10,20) :: y\n"
            "  real, dimension(10) :: result\n"
            "  integer :: i\n"
            "  integer :: j\n"
            "\n"
            "  do i = 1, 5, 1\n"
            "    y(i,1) = 0.0\n"
            "    do j = 1, 10, 1\n"
            "      y(i,1) = y(i,1) + x(i,j,idx) * y(j,idx)\n"
            "    enddo\n"
            "  enddo\n"
            "\n"
            "end subroutine my_kern" in result)
    assert Compile(tmpdir).string_compiles(result)
示例#9
0
def test_loop_create_invalid():
    '''Test that the create method in a Loop class raises the expected
    exception if the provided input is invalid.

    '''
    zero = Literal("0", INTEGER_SINGLE_TYPE)
    one = Literal("1", INTEGER_SINGLE_TYPE)
    children = Assignment.create(
        Reference(DataSymbol("x", INTEGER_SINGLE_TYPE)), one.copy())

    # invalid variable (test_check_variable tests check all ways a
    # variable could be invalid. Here we just check that the
    # _check_variable() method is called correctly)
    with pytest.raises(GenerationError) as excinfo:
        _ = Loop.create(1, zero, one, one, [children.copy()])
    assert ("variable property in Loop class should be a DataSymbol but "
            "found 'int'.") in str(excinfo.value)

    variable = DataSymbol("i", INTEGER_TYPE)

    # start not a Node.
    with pytest.raises(GenerationError) as excinfo:
        _ = Loop.create(variable, "invalid", one, one, [children.copy()])
    assert ("Item 'str' can't be child 0 of 'Loop'. The valid format is: "
            "'DataNode, DataNode, DataNode, Schedule'.") in str(excinfo.value)

    # stop not a Node.
    with pytest.raises(GenerationError) as excinfo:
        _ = Loop.create(variable, zero, "invalid", one, [children.copy()])
    assert ("Item 'str' can't be child 1 of 'Loop'. The valid format is: "
            "'DataNode, DataNode, DataNode, Schedule'.") in str(excinfo.value)

    # step not a Node.
    with pytest.raises(GenerationError) as excinfo:
        _ = Loop.create(variable, zero, one, "invalid", [children.copy()])
    assert ("Item 'str' can't be child 2 of 'Loop'. The valid format is: "
            "'DataNode, DataNode, DataNode, Schedule'.") in str(excinfo.value)

    # children not a list
    with pytest.raises(GenerationError) as excinfo:
        _ = Loop.create(variable, zero, one, one, "invalid")
    assert ("children argument in create method of Loop class should "
            "be a list but found 'str'." in str(excinfo.value))

    # contents of children list are not Node.
    with pytest.raises(GenerationError) as excinfo:
        _ = Loop.create(variable, zero, one, one, ["invalid"])
    assert ("Item 'str' can't be child 0 of 'Schedule'. The valid format is: "
            "'[Statement]*'." in str(excinfo.value))
示例#10
0
def test_range_out_of_order_setter():
    ''' Test that setting the start/stop/step props out of order raises the
    expected error. '''
    erange = Range()
    datanode1 = Literal("1", INTEGER_SINGLE_TYPE)
    datanode2 = Literal("2", INTEGER_SINGLE_TYPE)
    datanode3 = Literal("3", INTEGER_SINGLE_TYPE)

    # Stop before Start
    with pytest.raises(IndexError) as excinfo:
        erange.stop = datanode2
    assert ("The Stop value 'Literal[value:'2', Scalar<INTEGER, SINGLE>]' can"
            " not be inserted into range 'Range[]' before the Start value is "
            "provided." in str(excinfo.value))
    # Once start is added, setting it up again just replaces it
    erange.start = datanode1
    erange.start = datanode1.copy()
    assert len(erange.children) == 1
    # Now Stop can be accepted
    erange.stop = datanode2
    # Once added, setting it up again just replaces it
    erange.stop = datanode2.copy()
    assert len(erange.children) == 2

    # Step before Step
    del erange.children[1]
    with pytest.raises(IndexError) as excinfo:
        erange.step = datanode3
    assert ("The Step value 'Literal[value:'3', Scalar<INTEGER, SINGLE>]' can"
            " not be inserted into range 'Range[]' before the Start and Stop "
            "values are provided." in str(excinfo.value))
    erange.stop = datanode2
    erange.step = datanode3
    # Once added, setting it up again just replaces it
    erange.step = datanode3.copy()
    assert len(erange.children) == 3
示例#11
0
def test_literal_can_be_copied():
    ''' Test that a Literal node can be copied. '''

    literal = Literal("1", INTEGER_SINGLE_TYPE)

    literal1 = literal.copy()
    assert isinstance(literal1, Literal)
    assert literal1 is not literal
    assert literal1.value == "1"
    assert literal1.datatype is INTEGER_SINGLE_TYPE

    # Modifying the new literal does not affect the original
    literal1._value = "2"
    assert literal1.value == "2"
    assert literal.value == "1"
示例#12
0
def test_auto_invoke_no_return(capsys):
    ''' Check that using the auto-invoke profiling option does not add any
    profiling if the invoke contains a Return anywhere other than as the
    last statement. '''
    Profiler.set_options([Profiler.INVOKES])
    symbol_table = SymbolTable()
    arg1 = symbol_table.new_symbol(
        symbol_type=DataSymbol, datatype=REAL_TYPE)
    zero = Literal("0.0", REAL_TYPE)
    assign1 = Assignment.create(Reference(arg1), zero)
    assign2 = Assignment.create(Reference(arg1), zero.copy())

    # Create Schedule with Return at the start.
    kschedule = KernelSchedule.create(
        "work1", symbol_table, [Return(), assign1, assign2])
    Profiler.add_profile_nodes(kschedule, Loop)
    # No profiling should have been added
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work1' because it contains one "
            "or more Return statements" in err)

    # Create Schedule with Return in the middle.
    kschedule = KernelSchedule.create(
        "work2", symbol_table, [assign1.copy(), Return(), assign2.copy()])
    Profiler.add_profile_nodes(kschedule, Loop)
    # No profiling should have been added
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work2' because it contains one "
            "or more Return statements" in err)

    # Create Schedule with a Return at the end as well as in the middle.
    kschedule = KernelSchedule.create(
        "work3", symbol_table, [assign1.copy(), Return(), assign2.copy(),
                                Return()])
    Profiler.add_profile_nodes(kschedule, Loop)
    # No profiling should have been added
    assert not kschedule.walk(ProfileNode)
    _, err = capsys.readouterr()
    assert ("Not adding profiling to routine 'work3' because it contains one "
            "or more Return statements" in err)
示例#13
0
def test_array_indices():
    ''' Tests for the indices property (provided by the ArrayMixin class). '''
    one = Literal("1", INTEGER_TYPE)
    array = ArrayReference.create(
        DataSymbol("test", ArrayType(REAL_TYPE, [10])), [one])
    assert array.indices == [one]
    # Add an invalid child
    array._children = [one.copy(), "hello"]
    with pytest.raises(InternalError) as err:
        _ = array.indices
    assert ("ArrayReference malformed or incomplete: child 1 must by a "
            "psyir.nodes.DataNode or Range representing an array-index "
            "expression but found 'str'" in str(err.value))
    # Remove the children altogether
    array._children = []
    with pytest.raises(InternalError) as err:
        _ = array.indices
    assert ("ArrayReference malformed or incomplete: must have one or more "
            "children representing array-index expressions but found none"
            in str(err.value))
示例#14
0
def create_range(array_symbol, dim):
    '''Utility routine that creates and returns a Range Node that
    specifies the full range of the supplied dimension (dim) in the
    array (array_symbol). This is done using the LBOUND and UBOUND
    intrinsics.

    :param array_symbol: the array of interest.
    :type array_symbol: :py:class:`psyclone.psyir.symbol.DataSymbol`
    :param int dim: the dimension of interest in the array.

    :returns: a range node specifying the full range of the supplied \
        array dimension.
    :rtype: :py:class:`psyclone.psyir.nodes.Range`

    '''
    int_dim = Literal(str(dim), INTEGER_TYPE)
    lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                    Reference(array_symbol), int_dim)
    ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                    Reference(array_symbol), int_dim.copy())
    return Range.create(lbound, ubound)
示例#15
0
def test_array_is_full_range():
    '''Test that the is_full_range method in the Array Node works as
    expected. '''
    # pylint: disable=too-many-statements
    zero = Literal("0", INTEGER_SINGLE_TYPE)
    one = Literal("1", INTEGER_SINGLE_TYPE)
    array_type = ArrayType(REAL_SINGLE_TYPE, [10])
    symbol = DataSymbol("my_array", array_type)
    reference = Reference(symbol)
    lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND, reference,
                                    one)
    ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                    reference.copy(), one.copy())
    symbol_error = DataSymbol("another_array", array_type)
    reference_error = Reference(symbol_error)

    # Index out of bounds
    array_reference = ArrayReference.create(symbol, [one.copy()])
    with pytest.raises(ValueError) as excinfo:
        array_reference.is_full_range(1)
    assert ("In ArrayReference 'my_array' the specified index '1' must be "
            "less than the number of dimensions '1'." in str(excinfo.value))

    # Array dimension is not a Range
    assert not array_reference.is_full_range(0)

    # Check LBOUND
    # Array dimension range lower bound is not a binary operation
    my_range = Range.create(one.copy(), one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range lower bound is not an LBOUND binary operation
    my_range = Range.create(ubound, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range lower bound is an LBOUND binary operation
    # with the first value not being a reference
    lbound_error = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                          zero.copy(), zero.copy())
    my_range = Range.create(lbound_error, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range lower bound is an LBOUND binary operation
    # with the first value being a reference to a different symbol
    lbound_error = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                          reference_error, zero.copy())
    my_range = Range.create(lbound_error, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range lower bound is an LBOUND binary operation
    # with the second value not being a literal.
    lbound_error = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                          reference.copy(), reference.copy())
    my_range = Range.create(lbound_error, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range lower bound is an LBOUND binary operation
    # with the second value not being an integer literal.
    lbound_error = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                          reference.copy(),
                                          Literal("1.0", REAL_SINGLE_TYPE))
    my_range = Range.create(lbound_error, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range lower bound is an LBOUND binary operation
    # with the second value being an integer literal with the wrong
    # value (should be 0 as this dimension index is 0).
    lbound_error = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                          reference.copy(), one.copy())
    my_range = Range.create(lbound_error, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Check UBOUND
    # Array dimension range upper bound is not a binary operation
    my_range = Range.create(lbound, one.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range upper bound is not a UBOUND binary operation
    my_range = Range.create(lbound.copy(), lbound.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range upper bound is a UBOUND binary operation
    # with the first value not being a reference
    ubound_error = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                          zero.copy(), zero.copy())
    my_range = Range.create(lbound.copy(), ubound_error, one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range upper bound is a UBOUND binary operation
    # with the first value being a reference to a different symbol
    ubound_error = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                          reference_error.copy(), zero.copy())
    my_range = Range.create(lbound.copy(), ubound_error, one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range upper bound is a UBOUND binary operation
    # with the second value not being a literal.
    ubound_error = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                          reference.copy(), reference.copy())
    my_range = Range.create(lbound.copy(), ubound_error, one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range upper bound is a UBOUND binary operation
    # with the second value not being an integer literal.
    ubound_error = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                          reference.copy(),
                                          Literal("1.0", REAL_SINGLE_TYPE))
    my_range = Range.create(lbound.copy(), ubound_error, one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range upper bound is a UBOUND binary operation
    # with the second value being an integer literal with the wrong
    # value (should be 1 as this dimension is 1).
    ubound_error = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                          reference.copy(), zero.copy())
    my_range = Range.create(lbound.copy(), ubound_error, one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Check Step
    # Array dimension range step is not a literal.
    my_range = Range.create(lbound.copy(), ubound.copy(), lbound.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range step is not an integer literal.
    my_range = Range.create(lbound.copy(), ubound.copy(), one.copy())
    # We have to change this to a non-integer manually as the create
    # function only accepts integer literals for the step argument.
    my_range.children[2] = Literal("1.0", REAL_SINGLE_TYPE)
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # Array dimension range step is is an integer literal with the
    # wrong value (not 1).
    my_range = Range.create(lbound.copy(), ubound.copy(), zero.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert not array_reference.is_full_range(0)

    # All is as it should be.
    # The full range is covered so return true.
    my_range = Range.create(lbound.copy(), ubound.copy(), one.copy())
    array_reference = ArrayReference.create(symbol, [my_range])
    assert array_reference.is_full_range(0)
示例#16
0
def test_array_is_upper_bound():
    '''Test that the is_upper_bound method in the Array Node works as
    expected.

    '''
    one = Literal("1", INTEGER_TYPE)
    array = ArrayReference.create(
        DataSymbol("test", ArrayType(REAL_TYPE, [10])), [one])
    with pytest.raises(TypeError) as info:
        array.is_upper_bound("hello")
    assert ("The index argument should be an integer but found 'str'."
            in str(info.value))

    # not a range node at index 0
    assert not array.is_upper_bound(0)

    # range node does not have a binary operator for its stop value
    array.children[0] = Range.create(one.copy(), one.copy(), one.copy())
    assert not array.is_upper_bound(0)

    # range node ubound references a different array
    array2 = ArrayReference.create(
        DataSymbol("test2", ArrayType(REAL_TYPE, [10])), [one.copy()])
    operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND, array2,
                                      one.copy())
    array.children[0] = Range.create(one.copy(), operator, one.copy())
    assert not array.is_upper_bound(0)

    # range node ubound references a different index
    operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                      array.copy(), Literal("2", INTEGER_TYPE))
    array.children[0] = Range.create(one.copy(), operator, one.copy())
    assert not array.is_upper_bound(0)

    # all is well
    operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                      array.copy(), one.copy())
    array.children[0] = Range.create(one.copy(), operator, one.copy())
    assert array.is_upper_bound(0)
示例#17
0
def test_is_array_range():
    '''test that the is_array_range method behaves as expected, returning
    true if the LHS of the assignment is an array range access.

    '''
    one = Literal("1.0", REAL_TYPE)
    int_one = Literal("1", INTEGER_TYPE)
    int_ten = Literal("10", INTEGER_TYPE)

    # lhs is an array reference with a range
    array_type = ArrayType(REAL_TYPE, [10, 10])
    symbol = DataSymbol("x", array_type)
    x_range = Range.create(int_one, int_ten.copy(), int_one.copy())
    array_ref = ArrayReference.create(symbol, [x_range, int_one.copy()])
    assignment = Assignment.create(array_ref, one.copy())
    assert assignment.is_array_range is True

    # Check when lhs consists of various forms of structure access
    grid_type = StructureType.create([
        ("dx", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC),
        ("dy", REAL_SINGLE_TYPE, Symbol.Visibility.PUBLIC)
    ])
    grid_type_symbol = DataTypeSymbol("grid_type", grid_type)
    # Create the definition of the 'field_type', contains array of grid_types
    field_type_def = StructureType.create([
        ("data", ArrayType(REAL_SINGLE_TYPE, [10]), Symbol.Visibility.PUBLIC),
        ("sub_meshes", ArrayType(grid_type_symbol,
                                 [3]), Symbol.Visibility.PUBLIC)
    ])
    field_type_symbol = DataTypeSymbol("field_type", field_type_def)
    field_symbol = DataSymbol("wind", field_type_symbol)

    # Array reference to component of derived type using a range
    lbound = BinaryOperation.create(
        BinaryOperation.Operator.LBOUND,
        StructureReference.create(field_symbol, ["data"]), int_one.copy())
    ubound = BinaryOperation.create(
        BinaryOperation.Operator.UBOUND,
        StructureReference.create(field_symbol, ["data"]), int_one.copy())
    my_range = Range.create(lbound, ubound)

    data_ref = StructureReference.create(field_symbol, [("data", [my_range])])
    assign = Assignment.create(data_ref, one.copy())
    assert assign.is_array_range is True

    # Access to slice of 'sub_meshes': wind%sub_meshes(1:3)%dx = 1.0
    sub_range = Range.create(int_one.copy(), Literal("3", INTEGER_TYPE))
    dx_ref = StructureReference.create(field_symbol,
                                       [("sub_meshes", [sub_range]), "dx"])
    sub_assign = Assignment.create(dx_ref, one.copy())
    assert sub_assign.is_array_range is True

    # Create an array of these derived types and assign to a slice:
    # chi(1:10)%data(1) = 1.0
    field_bundle_symbol = DataSymbol("chi", ArrayType(field_type_symbol, [3]))
    fld_range = Range.create(int_one.copy(), Literal("10", INTEGER_TYPE))
    fld_ref = ArrayOfStructuresReference.create(field_bundle_symbol,
                                                [fld_range],
                                                [("data", [int_one.copy()])])
    fld_assign = Assignment.create(fld_ref, one.copy())
    assert fld_assign.is_array_range is True

    # When the slice has two operator ancestors, none of which are a reduction
    # e.g y(1, INT(ABS(map(:, 1)))) = 1.0
    int_array_type = ArrayType(INTEGER_SINGLE_TYPE, [10, 10])
    map_sym = DataSymbol("map", int_array_type)
    lbound1 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(map_sym), int_one.copy())
    ubound1 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(map_sym), int_one.copy())
    my_range1 = Range.create(lbound1, ubound1)
    abs_op = UnaryOperation.create(
        UnaryOperation.Operator.ABS,
        ArrayReference.create(map_sym, [my_range1, int_one.copy()]))
    int_op = UnaryOperation.create(UnaryOperation.Operator.INT, abs_op)
    assignment = Assignment.create(
        ArrayReference.create(symbol, [int_one.copy(), int_op]), one.copy())
    assert assignment.is_array_range is True