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
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"
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"
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 and false otherwise. ''' one = Literal("1.0", REAL_TYPE) int_one = Literal("1", INTEGER_TYPE) var = DataSymbol("x", REAL_TYPE) reference = Reference(var) # lhs is not an array assignment = Assignment.create(reference, one) assert not assignment.is_array_range # lhs is an array reference but has no range array_type = ArrayType(REAL_TYPE, [10, 10]) symbol = DataSymbol("x", array_type) array_ref = ArrayReference(symbol, [1, 3]) assignment = Assignment.create(array_ref, one) assert not assignment.is_array_range # lhs is an array reference with a range my_range = Range.create(int_one, int_one, int_one) array_ref = ArrayReference.create(symbol, [my_range, int_one]) assignment = Assignment.create(array_ref, one) assert assignment.is_array_range
def test_range_references_props(): ''' Test that the properties of a Range return what we expect when the start, stop and step are references or expressions. ''' from psyclone.psyir.nodes import BinaryOperation, KernelSchedule sched = KernelSchedule("test_sched") sym_table = sched.symbol_table start_symbol = DataSymbol("istart", INTEGER_SINGLE_TYPE) stop_symbol = DataSymbol("istop", INTEGER_SINGLE_TYPE) step_symbol = DataSymbol("istep", INTEGER_SINGLE_TYPE) sym_table.add(start_symbol) sym_table.add(stop_symbol) sym_table.add(step_symbol) startvar = Reference(start_symbol) stopvar = Reference(stop_symbol) start = BinaryOperation.create(BinaryOperation.Operator.SUB, startvar, Literal("1", INTEGER_SINGLE_TYPE)) stop = BinaryOperation.create(BinaryOperation.Operator.ADD, stopvar, Literal("1", INTEGER_SINGLE_TYPE)) step = Reference(step_symbol) erange = Range.create(start, stop, step) assert erange.start is start assert erange.stop is stop assert erange.step is step assert erange.children[0] is start assert erange.children[1] is stop assert erange.children[2] is step
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)
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
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_array_is_lower_bound(): '''Test that the is_lower_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_lower_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_lower_bound(0) # range node does not have a binary operator for its start value array.children[0] = Range.create(one, one, one) assert not array.is_lower_bound(0) # range node lbound references a different array array2 = ArrayReference.create(DataSymbol("test2", ArrayType(REAL_TYPE, [10])), [one]) operator = BinaryOperation.create( BinaryOperation.Operator.LBOUND, array2, Literal("1", INTEGER_TYPE)) array.children[0] = Range.create(operator, one, one) assert not array.is_lower_bound(0) # range node lbound references a different index operator = BinaryOperation.create( BinaryOperation.Operator.LBOUND, array, Literal("2", INTEGER_TYPE)) array.children[0] = Range.create(operator, one, one) assert not array.is_lower_bound(0) # all is well operator = BinaryOperation.create( BinaryOperation.Operator.LBOUND, array, one) array.children[0] = Range.create(operator, one, one) assert array.is_lower_bound(0)
def test_range_create(): ''' Check that the Range.create() method behaves as intended. ''' parent = Node() start = Literal("1", INTEGER_SINGLE_TYPE) stop = Literal("10", INTEGER_SINGLE_TYPE) # No parent and no step erange = Range.create(start, stop) assert erange.children[0] is start assert erange.children[1] is stop assert erange.parent is None # Parent but no step erange2 = Range.create(start, stop, parent=parent) assert erange2.parent is parent assert erange2.children[2].value == "1" # Parent and step supplied erange3 = Range.create(start, stop, step=Literal("5", INTEGER_SINGLE_TYPE), parent=parent) assert erange3.parent is parent assert erange3.children[2].value == "5"
def test_range_literals_props(): ''' Test that the properties of a Range return what we expect when the start, stop and step are Literals. ''' start = Literal("10", INTEGER_SINGLE_TYPE) stop = Literal("20", INTEGER_SINGLE_TYPE) erange = Range.create(start, stop) assert erange.children[0] is start assert erange.children[1] is stop # We didn't supply an increment so check that one was created assert isinstance(erange.children[2], Literal) assert ( erange.children[2].datatype.intrinsic == ScalarType.Intrinsic.INTEGER) assert (erange.children[2].datatype.precision == ScalarType.Precision.UNDEFINED) assert erange.children[2].value == "1" # Create another one with a specified step erange2 = Range.create(start, stop, Literal("5", INTEGER_SINGLE_TYPE)) assert erange2.children[0] is start assert erange2.children[1] is stop assert ( erange2.children[2].datatype.precision == ScalarType.Precision.SINGLE) assert erange2.step.value == "5"
def create_stepped_range(symbol): '''Utility routine that creates and returns a Range Node that specifies a range from "2" to "symbol" step "2". :param symbol: the symbol representing the upper bound. :type symbol: :py:class:`psyclone.psyir.symbol.Symbol` :returns: a range node specifying a range from 2 to "symbol" with \ a step of 2 for the supplied array dimension. :rtype: :py:class:`psyclone.psyir.nodes.Range` ''' lbound = Literal("2", INTEGER_TYPE) ubound = Reference(symbol) step = Literal("2", INTEGER_TYPE) return Range.create(lbound, ubound, step)
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))
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) return Range.create(lbound, ubound)
def test_range_view(capsys): ''' Check that calling view() on an array with a child Range works as expected. ''' # 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 = ArrayReference.create(DataSymbol("my_array", array_type), [Literal("1", INTEGER_SINGLE_TYPE), erange]) array.view() stdout, _ = capsys.readouterr() arrayref = colored("ArrayReference", ArrayReference._colour) literal = colored("Literal", Literal._colour) rangestr = colored("Range", Range._colour) 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)
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)
INT_ONE = Literal("1", INTEGER8_TYPE) # Reference to the "flag" scalar component of FIELD_SYMBOL, "field%flag" FLAG_REF = StructureReference.create(FIELD_SYMBOL, ["flag"]) # Reference to "field%grid%dx" DX_REF = StructureReference.create(FIELD_SYMBOL, ["grid", "dx"]) # Array reference to component of derived type using a range LBOUND = BinaryOperation.create( BinaryOperation.Operator.LBOUND, StructureReference.create(FIELD_SYMBOL, ["data"]), INT_ONE) UBOUND = BinaryOperation.create( BinaryOperation.Operator.UBOUND, StructureReference.create(FIELD_SYMBOL, ["data"]), INT_ONE) MY_RANGE = Range.create(LBOUND, UBOUND) DATA_REF = StructureReference.create(FIELD_SYMBOL, [("data", [MY_RANGE])]) # Reference to "field%sub_meshes(1)%dx" DX_REF2 = StructureReference.create(FIELD_SYMBOL, [("sub_meshes", [INT_ONE]), "dx"]) # Reference to "chi(1)%sub_meshes(1)%dx" DX_REF3 = ArrayOfStructuresReference.create(FIELD_BUNDLE_SYMBOL, [INT_ONE], [("sub_meshes", [INT_ONE]), "dx"]) ASSIGNMENTS = [ Assignment.create(DX_REF, TWO), Assignment.create(FLAG_REF, INT_ONE), Assignment.create(DATA_REF, TWO),
def create_psyir_tree(): ''' Create an example PSyIR Tree. :returns: an example PSyIR tree. :rtype: :py:class:`psyclone.psyir.nodes.Container` ''' # Symbol table, symbols and scalar datatypes symbol_table = SymbolTable() arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) symbol_table.specify_argument_list([arg1]) tmp_symbol = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_DOUBLE_TYPE) index_symbol = symbol_table.new_symbol(root_name="i", symbol_type=DataSymbol, datatype=INTEGER4_TYPE) real_kind = symbol_table.new_symbol(root_name="RKIND", symbol_type=DataSymbol, datatype=INTEGER_TYPE, constant_value=8) routine_symbol = RoutineSymbol("my_sub") # Array using precision defined by another symbol scalar_type = ScalarType(ScalarType.Intrinsic.REAL, real_kind) array = symbol_table.new_symbol(root_name="a", symbol_type=DataSymbol, datatype=ArrayType(scalar_type, [10])) # Nodes which do not have Nodes as children and (some) predefined # scalar datatypes # TODO: Issue #1136 looks at how to avoid all of the _x versions zero_1 = Literal("0.0", REAL_TYPE) zero_2 = Literal("0.0", REAL_TYPE) zero_3 = Literal("0.0", REAL_TYPE) one_1 = Literal("1.0", REAL4_TYPE) one_2 = Literal("1.0", REAL4_TYPE) one_3 = Literal("1.0", REAL4_TYPE) two = Literal("2.0", scalar_type) int_zero = Literal("0", INTEGER_SINGLE_TYPE) int_one_1 = Literal("1", INTEGER8_TYPE) int_one_2 = Literal("1", INTEGER8_TYPE) int_one_3 = Literal("1", INTEGER8_TYPE) int_one_4 = Literal("1", INTEGER8_TYPE) tmp1_1 = Reference(arg1) tmp1_2 = Reference(arg1) tmp1_3 = Reference(arg1) tmp1_4 = Reference(arg1) tmp1_5 = Reference(arg1) tmp1_6 = Reference(arg1) tmp2_1 = Reference(tmp_symbol) tmp2_2 = Reference(tmp_symbol) tmp2_3 = Reference(tmp_symbol) tmp2_4 = Reference(tmp_symbol) tmp2_5 = Reference(tmp_symbol) tmp2_6 = Reference(tmp_symbol) # Unary Operation oper = UnaryOperation.Operator.SIN unaryoperation_1 = UnaryOperation.create(oper, tmp2_1) unaryoperation_2 = UnaryOperation.create(oper, tmp2_2) # Binary Operation oper = BinaryOperation.Operator.ADD binaryoperation_1 = BinaryOperation.create(oper, one_1, unaryoperation_1) binaryoperation_2 = BinaryOperation.create(oper, one_2, unaryoperation_2) # Nary Operation oper = NaryOperation.Operator.MAX naryoperation = NaryOperation.create(oper, [tmp1_1, tmp2_3, one_3]) # Array reference using a range lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(array), int_one_1) ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(array), int_one_2) my_range = Range.create(lbound, ubound) tmparray = ArrayReference.create(array, [my_range]) # Assignments assign1 = Assignment.create(tmp1_2, zero_1) assign2 = Assignment.create(tmp2_4, zero_2) assign3 = Assignment.create(tmp2_5, binaryoperation_1) assign4 = Assignment.create(tmp1_3, tmp2_6) assign5 = Assignment.create(tmp1_4, naryoperation) assign6 = Assignment.create(tmparray, two) # Call call = Call.create(routine_symbol, [tmp1_5, binaryoperation_2]) # If statement if_condition = BinaryOperation.create(BinaryOperation.Operator.GT, tmp1_6, zero_3) ifblock = IfBlock.create(if_condition, [assign3, assign4]) # Loop loop = Loop.create(index_symbol, int_zero, int_one_3, int_one_4, [ifblock]) # KernelSchedule kernel_schedule = KernelSchedule.create( "work", symbol_table, [assign1, call, assign2, loop, assign5, assign6]) # Container container_symbol_table = SymbolTable() container = Container.create("CONTAINER", container_symbol_table, [kernel_schedule]) # Import data from another container external_container = ContainerSymbol("some_mod") container_symbol_table.add(external_container) external_var = DataSymbol("some_var", INTEGER_TYPE, interface=GlobalInterface(external_container)) container_symbol_table.add(external_var) routine_symbol.interface = GlobalInterface(external_container) container_symbol_table.add(routine_symbol) return container
def create_psyir_tree(): ''' Create an example PSyIR Tree. :returns: an example PSyIR tree. :rtype: :py:class:`psyclone.psyir.nodes.Container` ''' # Symbol table, symbols and scalar datatypes symbol_table = SymbolTable() arg1 = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) symbol_table.specify_argument_list([arg1]) tmp_symbol = symbol_table.new_symbol(symbol_type=DataSymbol, datatype=REAL_DOUBLE_TYPE) index_symbol = symbol_table.new_symbol(root_name="i", symbol_type=DataSymbol, datatype=INTEGER4_TYPE) real_kind = symbol_table.new_symbol(root_name="RKIND", symbol_type=DataSymbol, datatype=INTEGER_TYPE, constant_value=8) routine_symbol = RoutineSymbol("my_sub") # Array using precision defined by another symbol scalar_type = ScalarType(ScalarType.Intrinsic.REAL, real_kind) array = symbol_table.new_symbol(root_name="a", symbol_type=DataSymbol, datatype=ArrayType(scalar_type, [10])) # Make generators for nodes which do not have other Nodes as children, # with some predefined scalar datatypes def zero(): return Literal("0.0", REAL_TYPE) def one(): return Literal("1.0", REAL4_TYPE) def two(): return Literal("2.0", scalar_type) def int_zero(): return Literal("0", INTEGER_SINGLE_TYPE) def int_one(): return Literal("1", INTEGER8_TYPE) def tmp1(): return Reference(arg1) def tmp2(): return Reference(tmp_symbol) # Unary Operation oper = UnaryOperation.Operator.SIN unaryoperation = UnaryOperation.create(oper, tmp2()) # Binary Operation oper = BinaryOperation.Operator.ADD binaryoperation = BinaryOperation.create(oper, one(), unaryoperation) # Nary Operation oper = NaryOperation.Operator.MAX naryoperation = NaryOperation.create(oper, [tmp1(), tmp2(), one()]) # Array reference using a range lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(array), int_one()) ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(array), int_one()) my_range = Range.create(lbound, ubound) tmparray = ArrayReference.create(array, [my_range]) # Assignments assign1 = Assignment.create(tmp1(), zero()) assign2 = Assignment.create(tmp2(), zero()) assign3 = Assignment.create(tmp2(), binaryoperation) assign4 = Assignment.create(tmp1(), tmp2()) assign5 = Assignment.create(tmp1(), naryoperation) assign6 = Assignment.create(tmparray, two()) # Call call = Call.create(routine_symbol, [tmp1(), binaryoperation.copy()]) # If statement if_condition = BinaryOperation.create(BinaryOperation.Operator.GT, tmp1(), zero()) ifblock = IfBlock.create(if_condition, [assign3, assign4]) # Loop loop = Loop.create(index_symbol, int_zero(), int_one(), int_one(), [ifblock]) # KernelSchedule kernel_schedule = KernelSchedule.create( "work", symbol_table, [assign1, call, assign2, loop, assign5, assign6]) # Container container_symbol_table = SymbolTable() container = Container.create("CONTAINER", container_symbol_table, [kernel_schedule]) # Import data from another container external_container = ContainerSymbol("some_mod") container_symbol_table.add(external_container) external_var = DataSymbol("some_var", INTEGER_TYPE, interface=GlobalInterface(external_container)) container_symbol_table.add(external_var) routine_symbol.interface = GlobalInterface(external_container) container_symbol_table.add(routine_symbol) return container
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)
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