Ejemplo n.º 1
0
def create_expr(symbol_table):
    '''Utility routine that creates and returns an expression containing a
    number of array references containing range nodes. The expression
    looks like the following (using Fortran array notation):

    x(2:n:2)*z(1,2:n:2)+a(1)

    :param symbol_table: the symbol table to which we add the array \
        symbols.
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an expression containing 3 array references, 2 of which \
        contain ranges.
    :rtype: :py:class:`psyclone.psyir.nodes.BinaryOperation`

    '''
    array_symbol = DataSymbol("x", ArrayType(REAL_TYPE, [10]))
    symbol_table.add(array_symbol)
    symbol_n = symbol_table.lookup("n")
    array_x = Array.create(array_symbol, [create_stepped_range(symbol_n)])
    array_symbol = DataSymbol("z", ArrayType(REAL_TYPE, [10, 10]))
    symbol_table.add(array_symbol)
    array_z = Array.create(
        array_symbol,
        [Literal("1", INTEGER_TYPE),
         create_stepped_range(symbol_n)])
    array_symbol = DataSymbol("a", ArrayType(REAL_TYPE, [10]))
    array_a = Array.create(array_symbol, [Literal("1", INTEGER_TYPE)])
    symbol_table.add(array_symbol)
    mult = BinaryOperation.create(BinaryOperation.Operator.MUL, array_x,
                                  array_z)
    add = BinaryOperation.create(BinaryOperation.Operator.ADD, mult, array_a)
    return add
Ejemplo n.º 2
0
def test_array_node_str():
    ''' Check the node_str method of the Array class.'''
    from psyclone.psyir.nodes.node import colored, SCHEDULE_COLOUR_MAP
    kschedule = KernelSchedule("kname")
    array_type = ArrayType(INTEGER_SINGLE_TYPE, [ArrayType.Extent.ATTRIBUTE])
    symbol = DataSymbol("aname", array_type)
    kschedule.symbol_table.add(symbol)
    assignment = Assignment(parent=kschedule)
    array = Array(symbol, parent=assignment)
    coloredtext = colored("ArrayReference", SCHEDULE_COLOUR_MAP["Reference"])
    assert coloredtext + "[name:'aname']" in array.node_str()
Ejemplo n.º 3
0
def test_cw_array():
    '''Check the CWriter class array method correctly prints
    out the C representation of an array.

    '''
    cwriter = CWriter()

    symbol = DataSymbol('a', REAL_TYPE)
    arr = Array(symbol)
    lit = Literal('0.0', REAL_TYPE)
    assignment = Assignment.create(arr, lit)

    # An array without any children (dimensions) should produce an error.
    with pytest.raises(VisitorError) as excinfo:
        result = cwriter(assignment)
    assert "Arrays must have at least 1 dimension but found node: '" \
        in str(excinfo.value)

    # Dimensions can be references, literals or operations
    arr.addchild(Reference(DataSymbol('b', INTEGER_TYPE), parent=arr))
    arr.addchild(Literal('1', INTEGER_TYPE, parent=arr))
    uop = UnaryOperation.create(UnaryOperation.Operator.MINUS,
                                Literal('2', INTEGER_TYPE))
    uop.parent = arr
    arr.addchild(uop)

    result = cwriter(assignment)
    # Results is reversed and flatten (row-major 1D)
    # dimensions are called <name>LEN<dimension> by convention
    assert result == "a[(-2) * aLEN2 * aLEN1 + 1 * aLEN1 + b] = 0.0;\n"
Ejemplo n.º 4
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 = Array.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, one)
    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"
Ejemplo n.º 5
0
def test_range_view(capsys):
    ''' Check that calling view() on an array with a child Range works
    as expected. '''
    from psyclone.psyir.nodes import Array
    from psyclone.psyir.nodes.node import colored, SCHEDULE_COLOUR_MAP
    # Create the PSyIR for 'my_array(1, 1:10)'
    erange = Range.create(Literal("1", INTEGER_SINGLE_TYPE),
                          Literal("10", INTEGER_SINGLE_TYPE))
    array_type = ArrayType(REAL_SINGLE_TYPE, [10, 10])
    array = Array.create(DataSymbol("my_array", array_type),
                         [Literal("1", INTEGER_SINGLE_TYPE), erange])
    array.view()
    stdout, _ = capsys.readouterr()
    arrayref = colored("ArrayReference",
                       SCHEDULE_COLOUR_MAP[array._colour_key])
    literal = colored("Literal",
                      SCHEDULE_COLOUR_MAP[array.children[0]._colour_key])
    rangestr = colored("Range", SCHEDULE_COLOUR_MAP[erange._colour_key])
    indent = "    "
    assert (arrayref + "[name:'my_array']\n" + indent + literal +
            "[value:'1', Scalar<INTEGER, SINGLE>]\n" + indent + rangestr +
            "[]\n" + 2 * indent + literal +
            "[value:'1', Scalar<INTEGER, SINGLE>]\n" + 2 * indent + literal +
            "[value:'10', Scalar<INTEGER, SINGLE>]\n" + 2 * indent + literal +
            "[value:'1', Scalar<INTEGER, UNDEFINED>]\n" in stdout)
Ejemplo n.º 6
0
def test_array_create_invalid3():
    '''Test that the create method in an Array class raises the expected
    exception if the provided input is invalid.

    '''
    # symbol argument is not a DataSymbol
    with pytest.raises(GenerationError) as excinfo:
        _ = Array.create([], [])
    assert ("symbol argument in create method of Array class should "
            "be a DataSymbol but found 'list'." in str(excinfo.value))

    # children not a list
    with pytest.raises(GenerationError) as excinfo:
        _ = Array.create(DataSymbol("temp", REAL_SINGLE_TYPE), "invalid")
    assert ("children argument in create method of Array class should "
            "be a list but found 'str'." in str(excinfo.value))
Ejemplo n.º 7
0
def test_array_can_be_printed():
    '''Test that an Array instance can always be printed (i.e. is
    initialised fully)'''
    kschedule = KernelSchedule("kname")
    symbol = DataSymbol("aname", INTEGER_SINGLE_TYPE)
    kschedule.symbol_table.add(symbol)
    assignment = Assignment(parent=kschedule)
    array = Array(symbol, assignment)
    assert "ArrayReference[name:'aname']\n" in str(array)
Ejemplo n.º 8
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)
    ubound1 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(mat_symbol), one)
    my_mat_range1 = Range.create(lbound1, ubound1, one)
    lbound2 = BinaryOperation.create(BinaryOperation.Operator.LBOUND,
                                     Reference(mat_symbol), two)
    ubound2 = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                     Reference(mat_symbol), two)
    my_mat_range2 = Range.create(lbound2, ubound2, one)
    matrix = Array.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)
    ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND,
                                    Reference(vec_symbol), one)
    my_vec_range = Range.create(lbound, ubound, one)
    vector = Array.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
Ejemplo n.º 9
0
def test_where_array_notation_rank():
    ''' Test that the _array_notation_rank() utility raises the expected
    errors when passed an unsupported Array object.
    '''
    array_type = ArrayType(REAL_TYPE, [10])
    symbol = DataSymbol("my_array", array_type)
    my_array = Array(symbol)
    processor = Fparser2Reader()
    with pytest.raises(NotImplementedError) as err:
        processor._array_notation_rank(my_array)
    assert ("Array reference in the PSyIR must have at least one child but "
            "'my_array'" in str(err.value))
    from psyclone.psyir.nodes import Range
    array_type = ArrayType(REAL_TYPE, [10])
    my_array = Array.create(DataSymbol("my_array", array_type),
                            [Range.create(Literal("1", INTEGER_TYPE),
                                          Literal("10", INTEGER_TYPE))])
    with pytest.raises(NotImplementedError) as err:
        processor._array_notation_rank(my_array)
    assert ("Only array notation of the form my_array(:, :, ...) is "
            "supported." in str(err.value))
Ejemplo n.º 10
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 = Array.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, one, one)
    assert not array.is_upper_bound(0)

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

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

    # all is well
    operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND, array,
                                      one)
    array.children[0] = Range.create(one, operator, one)
    assert array.is_upper_bound(0)
Ejemplo n.º 11
0
def test_cw_size():
    ''' Check the CWriter class SIZE method raises the expected error since
    there is no C equivalent. '''
    cwriter = CWriter()
    arr = Array(DataSymbol('a', INTEGER_TYPE))
    lit = Literal('1', INTEGER_TYPE)
    size = BinaryOperation.create(BinaryOperation.Operator.SIZE, arr, lit)
    lhs = Reference(DataSymbol('length', INTEGER_TYPE))
    assignment = Assignment.create(lhs, size)

    with pytest.raises(VisitorError) as excinfo:
        cwriter(assignment)
    assert ("C backend does not support the 'Operator.SIZE' operator"
            in str(excinfo.value))
Ejemplo n.º 12
0
def test_call_create():
    '''Test that the create method creates a valid call with arguments'''

    routine = RoutineSymbol("ellie")
    array_type = ArrayType(INTEGER_TYPE, shape=[10, 20])
    arguments = [
        Reference(DataSymbol("arg1", INTEGER_TYPE)),
        Array(DataSymbol("arg2", array_type))
    ]
    call = Call.create(routine, arguments)
    assert call.routine is routine
    for idx, child, in enumerate(call.children):
        assert child is arguments[idx]
        assert child.parent is call
Ejemplo n.º 13
0
def test_bound_intrinsic_wrong_type(bound):
    ''' Check that attempting to create an L/UBOUND intrinsic operator
    with the wrong type of arguments raises the expected error. '''
    with pytest.raises(TypeError) as err:
        # First argument must be an Array
        _ = BinaryOperation.create(bound,
                                   Literal("1", INTEGER_TYPE),
                                   Literal("1", INTEGER_TYPE))
    assert "must be an Array but got: 'Literal" in str(err.value)
    with pytest.raises(TypeError) as err:
        # Second argument cannot be a real literal
        _ = BinaryOperation.create(bound,
                                   Array("array"),
                                   Literal("1.0", REAL_TYPE))
    assert ("must be an integer but got a Literal of type REAL" in
            str(err.value))
Ejemplo n.º 14
0
def test_array_validate_index():
    '''Test that the validate_index utility function behaves as expected.'''
    array = Array.create(DataSymbol("test", ArrayType(REAL_TYPE, [10])),
                         [Literal("1", INTEGER_TYPE)])
    with pytest.raises(TypeError) as info:
        array._validate_index("hello")
    assert ("The index argument should be an integer but found 'str'."
            in str(info.value))

    with pytest.raises(ValueError) as info:
        array._validate_index(1)
    assert ("In Array 'test' the specified index '1' must be less than the "
            "number of dimensions '1'." in str(info.value))

    array._validate_index(0)
    array._validate_index(-1)
Ejemplo n.º 15
0
def test_array_children_validation():
    '''Test that children added to Array are validated. Array accepts
    DataNodes and Range children.'''
    array_type = ArrayType(REAL_SINGLE_TYPE, shape=[5, 5])
    array = Array(DataSymbol("rname", array_type))
    datanode1 = Literal("1", INTEGER_SINGLE_TYPE)
    erange = Range()
    assignment = Assignment()

    # Invalid child
    with pytest.raises(GenerationError) as excinfo:
        array.addchild(assignment)
    assert ("Item 'Assignment' can't be child 0 of 'ArrayReference'. The valid"
            " format is: '[DataNode | Range]*'." in str(excinfo.value))

    # Valid children
    array.addchild(datanode1)
    array.addchild(erange)
Ejemplo n.º 16
0
def test_array_create_invalid1():
    '''Test that the create method in the Array class raises an exception
    if the provided symbol is not an array.

    '''
    symbol_i = DataSymbol("i", INTEGER_SINGLE_TYPE)
    symbol_j = DataSymbol("j", INTEGER_SINGLE_TYPE)
    symbol_temp = DataSymbol("temp", REAL_SINGLE_TYPE)
    children = [
        Reference(symbol_i),
        Reference(symbol_j),
        Literal("1", INTEGER_SINGLE_TYPE)
    ]
    with pytest.raises(GenerationError) as excinfo:
        _ = Array.create(symbol_temp, children)
    assert ("expecting the symbol to be an array, not a scalar."
            in str(excinfo.value))
Ejemplo n.º 17
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 Array, but found "
            "'DataNode'." in str(info.value))

    array_symbol = DataSymbol("x", ArrayType(INTEGER_TYPE, [10, 10]))
    one = Literal("1", INTEGER_TYPE)
    array_assignment = Array.create(array_symbol, [one, one])
    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 Array with at least one "
        "of its dimensions being a Range, but found None in "
        "'ArrayArrayReference[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[]\\nArrayArrayReference["
        "name:'x']\\nRange[]\\nArrayArrayReference[name:'x']\\nRange[]\\n'."
        in str(info.value))
Ejemplo n.º 18
0
def test_array_create():
    '''Test that the create method in the Array class correctly
    creates an Array instance.

    '''
    array_type = ArrayType(REAL_SINGLE_TYPE, [10, 10, 10])
    symbol_temp = DataSymbol("temp", array_type)
    symbol_i = DataSymbol("i", INTEGER_SINGLE_TYPE)
    symbol_j = DataSymbol("j", INTEGER_SINGLE_TYPE)
    children = [
        Reference(symbol_i),
        Reference(symbol_j),
        Literal("1", INTEGER_SINGLE_TYPE)
    ]
    array = Array.create(symbol_temp, children)
    check_links(array, children)
    result = FortranWriter().array_node(array)
    assert result == "temp(i,j,1)"
Ejemplo n.º 19
0
def test_reference():
    '''Test that the common reference visitor writes the referenced symbol name
    or raises an error if the node is not Leaf node reference.

    '''
    # Generate PSyIR Reference
    test_visitor = PSyIRVisitor()
    reference = Reference(DataSymbol('a', REAL_TYPE))
    result = test_visitor(reference)
    assert result == "a"

    # Generate PSyIR Array
    reference2 = Array.create(DataSymbol('b', ArrayType(REAL_TYPE, shape=[1])),
                              [reference])
    with pytest.raises(VisitorError) as err:
        result = test_visitor(reference2)
    assert "Expecting a Reference with no children but found: " \
        in str(err.value)
Ejemplo n.º 20
0
def create_array_x(symbol_table):
    '''Utility routine that creates and returns an array reference to a
    one-dimensional array "x". The array reference accesses all of the
    elements in the array dimension using a range node. In Fortran
    array notation this is "x(:)".

    :param symbol_table: the symbol table to which we add the array \
        symbol.
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an array reference that accesses all elements of the \
        array "x".
    :rtype: :py:class:`psyclone.psyir.nodes.Array`

    '''
    array_symbol = DataSymbol("x", ArrayType(REAL_TYPE, [10]))
    symbol_table.add(array_symbol)
    return Array.create(array_symbol, [create_range(array_symbol, 1)])
Ejemplo n.º 21
0
def test_validate5():
    '''Check that the Matmul2Code validate method raises the expected
    exception when the supplied node is a MATMUL binary operation but
    either or both arguments are not references.

    '''
    trans = Matmul2CodeTrans()
    array_type = ArrayType(REAL_TYPE, [10])
    array = Array.create(DataSymbol("x", array_type),
                         [Literal("10", INTEGER_TYPE)])
    mult = BinaryOperation.create(BinaryOperation.Operator.MUL, array, array)
    matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, mult,
                                    mult)
    _ = Assignment.create(array, matmul)
    with pytest.raises(TransformationError) as excinfo:
        trans.validate(matmul)
    assert ("Expected children of a MATMUL BinaryOperation to be references, "
            "but found 'BinaryOperation', 'BinaryOperation'."
            in str(excinfo.value))
Ejemplo n.º 22
0
 def gen_code(self, parent):
     ''' Work out the appropriate loop bounds and then call the base
         class to generate the code '''
     self.start_expr = Literal("1", INTEGER_TYPE, parent=self)
     if self._loop_type == "colours":
         self.stop_expr = Reference(DataSymbol("ncolour", INTEGER_TYPE),
                                    parent=self)
     elif self._loop_type == "colour":
         self.stop_expr = Array(DataSymbol("ncp_ncolour", INTEGER_TYPE),
                                parent=self)
         self.stop_expr.addchild(Reference(
             DataSymbol("colour", INTEGER_TYPE)),
                                 parent=self.stop_expr)
     else:
         # This is a hack as the name is not a valid DataSymbol, it
         # is a call to a type-bound function.
         self.stop_expr = Reference(DataSymbol(
             self.field_name + "%get_ncell()", INTEGER_TYPE),
                                    parent=self)
     Loop.gen_code(self, parent)
Ejemplo n.º 23
0
def test_array_create_invalid2():
    '''Test that the create method in the Array class raises an exception
    if the number of dimension in the provided symbol is different to
    the number of indices provided to the create method.

    '''
    array_type = ArrayType(REAL_SINGLE_TYPE, [10])
    symbol_temp = DataSymbol("temp", array_type)
    symbol_i = DataSymbol("i", INTEGER_SINGLE_TYPE)
    symbol_j = DataSymbol("j", INTEGER_SINGLE_TYPE)
    children = [
        Reference(symbol_i),
        Reference(symbol_j),
        Literal("1", INTEGER_SINGLE_TYPE)
    ]
    with pytest.raises(GenerationError) as excinfo:
        _ = Array.create(symbol_temp, children)
    assert ("the symbol should have the same number of dimensions as indices "
            "(provided in the 'children' argument). Expecting '3' but found "
            "'1'." in str(excinfo.value))
Ejemplo n.º 24
0
def create_array_y_2d_slice(symbol_table):
    '''Utility routine that creates and returns an array reference to a 2
    dimensional array "y". The array reference accesses all elements
    in the 1st and 2nd dimensions of the array using range nodes. In
    Fortran array notation this is "y(:,:)".

    :param symbol_table: the symbol table to which we add the array \
        symbol.
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an array reference that accesses all elements of the 1st \
        and 2nd dimensions of the "y" array.
    :rtype: :py:class:`psyclone.psyir.nodes.Array`

    '''
    array_symbol = DataSymbol("y2", ArrayType(REAL_TYPE, [20, 10]))
    symbol_table.add(array_symbol)
    return Array.create(
        array_symbol,
        [create_range(array_symbol, 1),
         create_range(array_symbol, 2)])
Ejemplo n.º 25
0
def create_array_y(symbol_table):
    '''Utility routine that creates and returns an array reference to a
    two-dimensional array "y". The array reference accesses all
    elements in the 2nd dimension of the array using a range node and
    the n'th element of the 1st dimension. In Fortran array notation
    this is "y(n,:)".

    :param symbol_table: the symbol table to which we add the array \
        symbol and access the symbol "n".
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an array reference that accesses all elements of the \
        2nd dimension of array "y" and the n'th element of its 1st \
        dimension.
    :rtype: :py:class:`psyclone.psyir.nodes.Array`

    '''
    array_symbol = DataSymbol("y", ArrayType(REAL_TYPE, [10, 10]))
    symbol_table.add(array_symbol)
    return Array.create(
        array_symbol,
        [Reference(symbol_table.lookup("n")),
         create_range(array_symbol, 2)])
Ejemplo n.º 26
0
def create_array_y_slice_subset(symbol_table):
    '''Utility routine that creates and returns an array reference to a 2
    dimensional array "y". The array reference accesses elements 2 to
    "n" step 2 in the arrays 2nd dimension using a range node and the
    n'th element of the 1st dimension. In Fortran array notation this
    is "y(n,2:n:2)".

    :param symbol_table: the symbol table to which we add the array \
        symbol and access the symbol "n".
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an array reference that accesses elements 2 to "n" step \
        2 in the array "y"'s 2nd dimension and the n'th element of its \
        1st dimension.
    :rtype: :py:class:`psyclone.psyir.nodes.Array`

    '''
    array_symbol = DataSymbol("y3", ArrayType(REAL_TYPE, [10, 10]))
    symbol_table.add(array_symbol)
    symbol_n = symbol_table.lookup("n")
    return Array.create(array_symbol,
                        [Reference(symbol_n),
                         create_stepped_range(symbol_n)])
Ejemplo n.º 27
0
def create_array_z(symbol_table):
    '''Utility routine that creates and returns an array reference to a 3
    dimensional array "z". The array reference accesses all elements
    in the arrays 1st and 3rd dimensions using range nodes and
    accesses element "n" in its second dimension. In Fortran array
    notation this is "z(:,n,:)".

    :param symbol_table: the symbol table to which we add the array \
        symbol and access the symbol "n"
    :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable`

    :returns: an array reference that accesses all elements of the 1st \
        and 3rd dimensions of array "z" and the n'th element of its \
        second dimension.
    :rtype: :py:class:`psyclone.psyir.nodes.Array`

    '''
    array_symbol = DataSymbol("z", ArrayType(REAL_TYPE, [20, 10, 10]))
    symbol_table.add(array_symbol)
    return Array.create(array_symbol, [
        create_range(array_symbol, 1),
        Reference(symbol_table.lookup("n")),
        create_range(array_symbol, 3)
    ])
Ejemplo n.º 28
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,
                                    one)
    symbol_error = DataSymbol("another_array", array_type)
    reference_error = Reference(symbol_error)

    # Index out of bounds
    array_reference = Array.create(symbol, [one])
    with pytest.raises(ValueError) as excinfo:
        array_reference.is_full_range(1)
    assert ("In Array '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, one, one)
    array_reference = Array.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, one)
    array_reference = Array.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, zero)
    my_range = Range.create(lbound_error, one, one)
    array_reference = Array.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)
    my_range = Range.create(lbound_error, one, one)
    array_reference = Array.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, reference)
    my_range = Range.create(lbound_error, one, one)
    array_reference = Array.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,
                                          Literal("1.0", REAL_SINGLE_TYPE))
    my_range = Range.create(lbound_error, one, one)
    array_reference = Array.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, one)
    my_range = Range.create(lbound_error, one, one)
    array_reference = Array.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, one)
    array_reference = Array.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, lbound, one)
    array_reference = Array.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, zero)
    my_range = Range.create(lbound, ubound_error, one)
    array_reference = Array.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, zero)
    my_range = Range.create(lbound, ubound_error, one)
    array_reference = Array.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, reference)
    my_range = Range.create(lbound, ubound_error, one)
    array_reference = Array.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,
                                          Literal("1.0", REAL_SINGLE_TYPE))
    my_range = Range.create(lbound, ubound_error, one)
    array_reference = Array.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, zero)
    my_range = Range.create(lbound, ubound_error, one)
    array_reference = Array.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, ubound, lbound)
    array_reference = Array.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, ubound, one)
    # 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 = Array.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, ubound, zero)
    array_reference = Array.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, ubound, one)
    array_reference = Array.create(symbol, [my_range])
    assert array_reference.is_full_range(0)
Ejemplo n.º 29
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
Ejemplo n.º 30
0
    def apply(self, node, options=None):
        '''Apply the MATMUL intrinsic conversion transformation to the
        specified node. This node must be a MATMUL
        BinaryOperation. Currently only the matrix vector version of
        MATMUL is supported. The arguments are permitted to have
        additional dimensions (i.e. more than 2 for the matrix and
        more than 1 for the vector) but the matrix can only have two
        indices which are ranges and these must be the first two
        indices and the vector can only have one index that is a range
        and this must be the first index. Further, the ranges must be
        for the full index space for that dimension (i.e. array
        subsections are not supported). If the transformation is
        successful then an assignment which includes a MATMUL
        BinaryOperation node is converted to equivalent inline code.

        :param node: a MATMUL Binary-Operation node.
        :type node: :py:class:`psyclone.psyGen.BinaryOperation`
        :param options: a dictionary with options for transformations.
        :type options: dictionary of string:values or None

        '''
        # pylint: disable=too-many-locals
        self.validate(node)

        assignment = node.parent
        matrix = node.children[0]
        vector = node.children[1]
        result = node.parent.lhs
        result_symbol = result.symbol

        # Create new i and j loop iterators.
        symbol_table = node.scope.symbol_table
        i_loop_name = symbol_table.new_symbol_name("i")
        i_loop_symbol = DataSymbol(i_loop_name, INTEGER_TYPE)
        symbol_table.add(i_loop_symbol)
        j_loop_name = symbol_table.new_symbol_name("j")
        j_loop_symbol = DataSymbol(j_loop_name, INTEGER_TYPE)
        symbol_table.add(j_loop_symbol)

        # Create "result(i)"
        result_dims = [Reference(i_loop_symbol)]
        if len(result.children) > 1:
            # Add any additional dimensions (in case of an array slice)
            result_dims.extend(result.children[1:])
        result = Array.create(result_symbol, result_dims)
        # Create "vector(j)"
        vector_dims = [Reference(j_loop_symbol)]
        if len(vector.children) > 1:
            # Add any additional dimensions (in case of an array slice)
            vector_dims.extend(vector.children[1:])
        vector_array_reference = Array.create(vector.symbol, vector_dims)
        # Create "matrix(i,j)"
        array_dims = [Reference(i_loop_symbol), Reference(j_loop_symbol)]
        if len(matrix.children) > 2:
            # Add any additional dimensions (in case of an array slice)
            array_dims.extend(matrix.children[2:])
        matrix_array_reference = Array.create(matrix.symbol, array_dims)
        # Create "matrix(i,j) * vector(j)"
        multiply = BinaryOperation.create(BinaryOperation.Operator.MUL,
                                          matrix_array_reference,
                                          vector_array_reference)
        # Create "result(i) + matrix(i,j) * vector(j)"
        rhs = BinaryOperation.create(BinaryOperation.Operator.ADD, result,
                                     multiply)
        # Create "result(i) = result(i) + matrix(i,j) * vector(j)"
        assign = Assignment.create(result, rhs)
        # Create j loop and add the above code as a child
        # Work out the bounds
        lower_bound, upper_bound, step = _get_array_bound(vector, 0)
        jloop = Loop.create(j_loop_symbol, lower_bound, upper_bound, step,
                            [assign])
        # Create "result(i) = 0.0"
        assign = Assignment.create(result, Literal("0.0", REAL_TYPE))
        # Create i loop and add assigment and j loop as children
        lower_bound, upper_bound, step = _get_array_bound(matrix, 0)
        iloop = Loop.create(i_loop_symbol, lower_bound, upper_bound, step,
                            [assign, jloop])
        # Add the new code to the PSyIR
        iloop.parent = assignment.parent
        assignment.parent.children.insert(assignment.position, iloop)
        # remove the original matmul
        assignment.parent.children.remove(assignment)