def test_range_setter_errors(prop): ''' Check that the various setters reject values that are not sub-classes of Node. ''' # We use exec() so that we can use the pytest parameterisation of the # various properties we want to test # pylint:disable=exec-used,unused-variable erange = Range() with pytest.raises(TypeError) as err: exec("erange." + prop + " = 1") assert "must be a sub-class of Node but got" in str(err.value) val = Literal("1.0", REAL_SINGLE_TYPE) with pytest.raises(TypeError) as err: exec("erange." + prop + " = val") assert ("value of a Range is a Literal then it must be of type " "INTEGER but got Scalar<REAL, SINGLE>" in str(err.value))
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 start = Literal("10", INTEGER_SINGLE_TYPE) stop = Literal("20", INTEGER_SINGLE_TYPE) 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 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 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 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 assert len(erange.children) == 3
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_sched_children_validation(): '''Test that children added to Schedule are validated. Schedule accepts Statements as children. ''' schedule = Schedule() statement = Assignment() nonstatement = Range() # Invalid child with pytest.raises(GenerationError) as excinfo: schedule.addchild(nonstatement) assert ("Item 'Range' can't be child 0 of 'Schedule'. The valid" " format is: '[Statement]*'." in str(excinfo.value)) # Valid children schedule.addchild(statement)
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)
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 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 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_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 test_range_str(): ''' Check that node_str and str work correctly for a Range node. ''' erange = Range() assert 'Range[]' in erange.node_str(colour=False) assert 'Range' in erange.node_str(colour=True) assert erange.node_str(colour=False) == str(erange)
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
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_range_children_validation(): '''Test that children added to Range are validated. Range accepts 3 DataNodes. ''' erange = Range() datanode1 = Literal("1", INTEGER_SINGLE_TYPE) datanode2 = Literal("2", INTEGER_SINGLE_TYPE) datanode3 = Literal("3", INTEGER_SINGLE_TYPE) range2 = Range() # First child with pytest.raises(GenerationError) as excinfo: erange.addchild(range2) assert ("Item 'Range' can't be child 0 of 'Range'. The valid format is: " "'DataNode, DataNode, DataNode'." in str(excinfo.value)) erange.addchild(datanode1) # Second child with pytest.raises(GenerationError) as excinfo: erange.addchild(range2) assert ("Item 'Range' can't be child 1 of 'Range'. The valid format is: " "'DataNode, DataNode, DataNode'." in str(excinfo.value)) erange.addchild(datanode2) # Third child with pytest.raises(GenerationError) as excinfo: erange.addchild(range2) assert ("Item 'Range' can't be child 2 of 'Range'. The valid format is: " "'DataNode, DataNode, DataNode'." in str(excinfo.value)) erange.addchild(datanode3) # Additional children with pytest.raises(GenerationError) as excinfo: erange.addchild(datanode1) assert ("Item 'Literal' can't be child 3 of 'Range'. The valid format is:" " 'DataNode, DataNode, DataNode'." in str(excinfo.value))
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 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