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"
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 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_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_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
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))
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)
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))
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
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"
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)
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))
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)
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)
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)
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