def test_find_or_create_imported_symbol_2(): ''' Check that the _find_or_create_imported_symbol() method creates new symbols when appropriate. ''' # Create some suitable PSyIR from scratch symbol_table = SymbolTable() symbol_table.add(DataSymbol("tmp", REAL_TYPE)) kernel1 = KernelSchedule.create("mod_1", SymbolTable(), []) container = Container.create("container_name", symbol_table, [kernel1]) xvar = DataSymbol("x", REAL_TYPE) xref = Reference(xvar) assign = Assignment.create(xref, Literal("1.0", REAL_TYPE)) kernel1.addchild(assign) # We have no wildcard imports so there can be no symbol named 'undefined' with pytest.raises(SymbolError) as err: _ = _find_or_create_imported_symbol(assign, "undefined") assert "No Symbol found for name 'undefined'" in str(err.value) # We should be able to find the 'tmp' symbol in the parent Container sym = _find_or_create_imported_symbol(assign, "tmp") assert sym.datatype.intrinsic == ScalarType.Intrinsic.REAL # Add a wildcard import to the SymbolTable of the KernelSchedule new_container = ContainerSymbol("some_mod") new_container.wildcard_import = True kernel1.symbol_table.add(new_container) # Symbol not in any container but we do have wildcard imports so we # get a new symbol back new_symbol = _find_or_create_imported_symbol(assign, "undefined") assert new_symbol.name == "undefined" assert isinstance(new_symbol.interface, UnresolvedInterface) # pylint: disable=unidiomatic-typecheck assert type(new_symbol) == Symbol assert "undefined" not in container.symbol_table assert kernel1.symbol_table.lookup("undefined") is new_symbol
def create_expr(symbol_table): '''Utility routine that creates and returns an expression containing a number of array references containing range nodes. The expression looks like the following (using Fortran array notation): x(2:n:2)*z(1,2:n:2)+a(1) :param symbol_table: the symbol table to which we add the array \ symbols. :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable` :returns: an expression containing 3 array references, 2 of which \ contain ranges. :rtype: :py:class:`psyclone.psyir.nodes.BinaryOperation` ''' array_symbol = DataSymbol("x", ArrayType(REAL_TYPE, [10])) symbol_table.add(array_symbol) symbol_n = symbol_table.lookup("n") array_x = Array.create(array_symbol, [create_stepped_range(symbol_n)]) array_symbol = DataSymbol("z", ArrayType(REAL_TYPE, [10, 10])) symbol_table.add(array_symbol) array_z = Array.create( array_symbol, [Literal("1", INTEGER_TYPE), create_stepped_range(symbol_n)]) array_symbol = DataSymbol("a", ArrayType(REAL_TYPE, [10])) array_a = Array.create(array_symbol, [Literal("1", INTEGER_TYPE)]) symbol_table.add(array_symbol) mult = BinaryOperation.create(BinaryOperation.Operator.MUL, array_x, array_z) add = BinaryOperation.create(BinaryOperation.Operator.ADD, mult, array_a) return add
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_oclw_gen_declaration(): '''Check the OpenCLWriter class gen_declaration method produces the expected declarations. ''' oclwriter = OpenCLWriter() # Basic entry - Scalar are passed by value and don't have additional # qualifiers. symbol = DataSymbol("dummy1", INTEGER_TYPE) result = oclwriter.gen_declaration(symbol) assert result == "int dummy1" # Array argument has a memory qualifier (only __global for now) array_type = ArrayType(INTEGER_TYPE, [2, ArrayType.Extent.ATTRIBUTE, 2]) symbol = DataSymbol("dummy2", array_type) result = oclwriter.gen_declaration(symbol) assert result == "__global int * restrict dummy2" # Array with unknown intent array_type = ArrayType(INTEGER_TYPE, [2, ArrayType.Extent.ATTRIBUTE, 2]) symbol = DataSymbol("dummy2", array_type, interface=ArgumentInterface( ArgumentInterface.Access.UNKNOWN)) result = oclwriter.gen_declaration(symbol) assert result == "__global int * restrict dummy2"
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_check_variable(): '''Test the _check_variable utility method behaves as expected''' with pytest.raises(GenerationError) as info: Loop._check_variable(None) assert ("variable property in Loop class should be a DataSymbol but " "found 'NoneType'." in str(info.value)) with pytest.raises(GenerationError) as info: Loop._check_variable("hello") assert ("variable property in Loop class should be a DataSymbol but " "found 'str'." in str(info.value)) array_type = ArrayType(INTEGER_TYPE, shape=[10, 20]) array_symbol = DataSymbol("my_array", array_type) with pytest.raises(GenerationError) as info: Loop._check_variable(array_symbol) assert ("variable property in Loop class should be a ScalarType but " "found 'ArrayType'." in str(info.value)) scalar_symbol = DataSymbol("my_array", REAL_TYPE) with pytest.raises(GenerationError) as info: Loop._check_variable(scalar_symbol) assert ("variable property in Loop class should be a scalar integer but " "found 'REAL'." in str(info.value)) scalar_symbol = DataSymbol("my_array", INTEGER_TYPE) assert Loop._check_variable(scalar_symbol) is None
def example_psyir(create_expression): '''Utility function that creates a PSyIR tree containing an ABS intrinsic operator and returns the operator. :param function create_expresssion: function used to create the \ content of the ABS operator. :returns: PSyIR ABS operator instance. :rtype: :py:class:`psyclone.psyGen.UnaryOperation` ''' symbol_table = SymbolTable() name1 = symbol_table.new_symbol_name("arg") arg1 = DataSymbol(name1, REAL_TYPE, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) symbol_table.add(arg1) name2 = symbol_table.new_symbol_name() local = DataSymbol(name2, REAL_TYPE) symbol_table.add(local) symbol_table.specify_argument_list([arg1]) var1 = Reference(arg1) var2 = Reference(local) oper = UnaryOperation.Operator.ABS operation = UnaryOperation.create(oper, create_expression(var1)) assign = Assignment.create(var2, operation) _ = KernelSchedule.create("abs_example", symbol_table, [assign]) return operation
def test_create(): '''Test that the static create() method creates a NemoLoop instance and its children corectly. ''' variable = DataSymbol("ji", INTEGER_TYPE) start = Literal("2", INTEGER_TYPE) stop = Literal("10", INTEGER_TYPE) step = Literal("1", INTEGER_TYPE) x_var = DataSymbol("X", REAL_TYPE) children = [Assignment.create(Reference(x_var), Literal("3.0", REAL_TYPE))] nemo_loop = NemoLoop.create(variable, start, stop, step, children) assert isinstance(nemo_loop, NemoLoop) assert nemo_loop.loop_type == "lon" assert isinstance(nemo_loop.loop_body, Schedule) assert len(nemo_loop.loop_body.children) == 1 assert nemo_loop.loop_body.children[0] is children[0] assert children[0].parent is nemo_loop.loop_body assert nemo_loop.loop_body.parent is nemo_loop assert nemo_loop.variable is variable assert nemo_loop.start_expr is start assert nemo_loop.stop_expr is stop assert nemo_loop.step_expr is step writer = FortranWriter() result = writer(nemo_loop) assert ("do ji = 2, 10, 1\n" " X = 3.0\n" "enddo" in result)
def test_create_errors(): '''Test that the expected exceptions are raised when the arguments to the create method are invalid. ''' variable = DataSymbol("ji", INTEGER_TYPE) start = Literal("2", INTEGER_TYPE) stop = Literal("10", INTEGER_TYPE) step = Literal("1", INTEGER_TYPE) x_var = DataSymbol("X", REAL_TYPE) children = Assignment.create(Reference(x_var), Literal("3.0", REAL_TYPE)) with pytest.raises(GenerationError) as info: _ = NemoLoop.create(None, start, stop, step, [children.copy()]) assert ("Generation Error: variable property in Loop class should be a " "DataSymbol but found 'NoneType'" in str(info.value)) with pytest.raises(GenerationError) as info: _ = NemoLoop.create(variable, None, stop, step, [children.copy()]) assert ("Generation Error: Item 'NoneType' can't be child 0 of 'Loop'. " "The valid format is: 'DataNode, DataNode, DataNode, Schedule'." in str(info.value)) with pytest.raises(GenerationError) as info: _ = NemoLoop.create(variable, start, None, step, [children.copy()]) assert ("Generation Error: Item 'NoneType' can't be child 1 of 'Loop'. " "The valid format is: 'DataNode, DataNode, DataNode, Schedule'." in str(info.value)) with pytest.raises(GenerationError) as info: _ = NemoLoop.create(variable, start, stop, None, [children.copy()]) assert ("Generation Error: Item 'NoneType' can't be child 2 of 'Loop'. " "The valid format is: 'DataNode, DataNode, DataNode, Schedule'." in str(info.value)) with pytest.raises(GenerationError) as info: _ = NemoLoop.create(variable, start, stop, step, None) assert ("Generation Error: children argument in create method of NemoLoop " "class should be a list but found 'NoneType'." in str(info.value))
def test_datasymbol_copy_properties(): '''Test that the DataSymbol copy_properties method works as expected.''' array_type = ArrayType(REAL_SINGLE_TYPE, [1, 2]) symbol = DataSymbol("myname", array_type, constant_value=None, interface=ArgumentInterface( ArgumentInterface.Access.READWRITE)) # Check an exception is raised if an incorrect argument is passed in with pytest.raises(TypeError) as excinfo: symbol.copy_properties(None) assert ("Argument should be of type 'DataSymbol' but found 'NoneType'." "") in str(excinfo.value) new_symbol = DataSymbol("other_name", INTEGER_SINGLE_TYPE, constant_value=7) symbol.copy_properties(new_symbol) assert symbol.name == "myname" assert symbol.datatype.intrinsic == ScalarType.Intrinsic.INTEGER assert symbol.datatype.precision == ScalarType.Precision.SINGLE assert symbol.is_local assert isinstance(symbol.constant_value, Literal) assert symbol.constant_value.value == "7" assert ( symbol.constant_value.datatype.intrinsic == symbol.datatype.intrinsic) assert ( symbol.constant_value.datatype.precision == symbol.datatype.precision)
def test_apply_existing_names(): '''Check that the apply method uses existing iterators appropriately when their symbols are already defined. ''' _, invoke_info = get_invoke("implicit_do.f90", api=API, idx=0) schedule = invoke_info.schedule assignment = schedule[0] array_ref = assignment.lhs trans = NemoArrayRange2LoopTrans() symbol_table = schedule.symbol_table symbol_table.add(DataSymbol("ji", INTEGER_TYPE)) symbol_table.add(DataSymbol("jj", INTEGER_TYPE)) symbol_table.add(DataSymbol("jk", INTEGER_TYPE)) trans.apply(array_ref.children[2]) trans.apply(array_ref.children[1]) trans.apply(array_ref.children[0]) writer = FortranWriter() result = writer(schedule) assert ("do jk = 1, jpk, 1\n" " do jj = 1, jpj, 1\n" " do ji = 1, jpi, 1\n" " umask(ji,jj,jk) = 0.0e0\n" " enddo\n" " enddo\n" "enddo" in result)
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_assignment_children_validation(): '''Test that children added to assignment are validated. Assignment accepts just 2 children and both are DataNodes. ''' # Create method with lhs not a Node. with pytest.raises(GenerationError) as excinfo: _ = Assignment.create("invalid", Literal("0.0", REAL_SINGLE_TYPE)) assert ("Item 'str' can't be child 0 of 'Assignment'. The valid format " "is: 'DataNode, DataNode'.") in str(excinfo.value) # Create method with rhs not a Node. with pytest.raises(GenerationError) as excinfo: _ = Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), "invalid") assert ("Item 'str' can't be child 1 of 'Assignment'. The valid format " "is: 'DataNode, DataNode'.") in str(excinfo.value) # Direct assignment of a 3rd children assignment = Assignment.create( Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)) with pytest.raises(GenerationError) as excinfo: assignment.children.append(Literal("0.0", REAL_SINGLE_TYPE)) assert ("Item 'Literal' can't be child 2 of 'Assignment'. The valid format" " is: 'DataNode, DataNode'.") in str(excinfo.value)
def test_fusetrans_error_not_same_parent(): ''' Check that we reject attempts to fuse loops which don't share the same parent ''' from psyclone.transformations import LoopFuseTrans sch1 = Schedule() sch2 = Schedule() loop1 = Loop(variable=DataSymbol("i", INTEGER_TYPE), parent=sch1) loop2 = Loop(variable=DataSymbol("j", INTEGER_TYPE), parent=sch2) sch1.addchild(loop1) sch2.addchild(loop2) loop1.addchild(Literal("1", INTEGER_TYPE, parent=loop1)) # start loop1.addchild(Literal("10", INTEGER_TYPE, parent=loop1)) # stop loop1.addchild(Literal("1", INTEGER_TYPE, parent=loop1)) # step loop1.addchild(Schedule(parent=loop1)) # loop body loop2.addchild(Literal("1", INTEGER_TYPE, parent=loop2)) # start loop2.addchild(Literal("10", INTEGER_TYPE, parent=loop2)) # stop loop2.addchild(Literal("1", INTEGER_TYPE, parent=loop2)) # step loop2.addchild(Schedule(parent=loop2)) # loop body fuse = LoopFuseTrans() # Try to fuse loops with different parents with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. Loops do not have the " "same parent" in str(err.value))
def test_fusetrans_error_not_same_parent(): ''' Check that we reject attempts to fuse loops which don't share the same parent ''' loop1 = Loop.create(DataSymbol("i", INTEGER_TYPE), Literal("1", INTEGER_TYPE), Literal("10", INTEGER_TYPE), Literal("1", INTEGER_TYPE), [Return()]) sch1 = Schedule() sch1.addchild(loop1) sch2 = Schedule() loop2 = Loop.create(DataSymbol("j", INTEGER_TYPE), Literal("1", INTEGER_TYPE), Literal("10", INTEGER_TYPE), Literal("1", INTEGER_TYPE), [Return()]) sch2.addchild(loop2) fuse = LoopFuseTrans() # Try to fuse loops with different parents with pytest.raises(TransformationError) as err: fuse.validate(loop1, loop2) assert ("Error in LoopFuseTrans transformation. Loops do not have the " "same parent" in str(err.value))
def test_operations_can_be_copied(): ''' Test that an operation can be copied. ''' operands = [Reference(DataSymbol("tmp1", REAL_SINGLE_TYPE)), Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Reference(DataSymbol("tmp3", REAL_SINGLE_TYPE))] operation = NaryOperation.create(NaryOperation.Operator.MAX, operands) operation1 = operation.copy() assert isinstance(operation1, NaryOperation) assert operation1 is not operation assert operation1.operator is NaryOperation.Operator.MAX assert operation1.children[0].symbol.name == "tmp1" assert operation1.children[0] is not operands[0] assert operation1.children[0].parent is operation1 assert operation1.children[1].symbol.name == "tmp2" assert operation1.children[1] is not operands[1] assert operation1.children[1].parent is operation1 assert operation1.children[2].symbol.name == "tmp3" assert operation1.children[2] is not operands[2] assert operation1.children[2].parent is operation1 assert len(operation1.children) == 3 assert len(operation.children) == 3 # Modifying the new operation does not affect the original operation1._operator = NaryOperation.Operator.MIN operation1.children.pop() assert len(operation1.children) == 2 assert len(operation.children) == 3 assert operation1.operator is NaryOperation.Operator.MIN assert operation.operator is NaryOperation.Operator.MAX
def test_binaryoperation_create_invalid(): '''Test that the create method in a BinaryOperation class raises the expected exception if the provided input is invalid. ''' ref1 = Reference(DataSymbol("tmp1", REAL_SINGLE_TYPE)) ref2 = Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)) add = BinaryOperation.Operator.ADD # oper not a BinaryOperation.Operator. with pytest.raises(GenerationError) as excinfo: _ = BinaryOperation.create("invalid", ref1, ref2) assert ("oper argument in create method of BinaryOperation class should " "be a PSyIR BinaryOperation Operator but found 'str'." in str(excinfo.value)) # lhs not a Node. with pytest.raises(GenerationError) as excinfo: _ = BinaryOperation.create(add, "invalid", ref2) assert ("Item 'str' can't be child 0 of 'BinaryOperation'. The valid " "format is: 'DataNode, DataNode'.") in str(excinfo.value) # rhs not a Node. with pytest.raises(GenerationError) as excinfo: _ = BinaryOperation.create(add, ref1, "invalid") assert ("Item 'str' can't be child 1 of 'BinaryOperation'. The valid " "format is: 'DataNode, DataNode'.") in str(excinfo.value)
def test_cw_array(): '''Check the CWriter class array method correctly prints out the C representation of an array. ''' cwriter = CWriter() symbol = DataSymbol('a', REAL_TYPE) arr = ArrayReference(symbol) lit = Literal('0.0', REAL_TYPE) assignment = Assignment.create(arr, lit) # An array without any children (dimensions) should produce an error. with pytest.raises(VisitorError) as excinfo: result = cwriter(assignment) assert "Arrays must have at least 1 dimension but found node: '" \ in str(excinfo.value) # Dimensions can be references, literals or operations arr.addchild(Reference(DataSymbol('b', INTEGER_TYPE), parent=arr)) arr.addchild(Literal('1', INTEGER_TYPE, parent=arr)) uop = UnaryOperation.create(UnaryOperation.Operator.MINUS, Literal('2', INTEGER_TYPE)) uop.parent = arr arr.addchild(uop) result = cwriter(assignment) # Results is reversed and flatten (row-major 1D) # dimensions are called <name>LEN<dimension> by convention assert result == "a[(-2) * aLEN2 * aLEN1 + 1 * aLEN1 + b] = 0.0;\n"
def test_datasymbol_constant_value_setter(): '''Test that a DataSymbol constant value can be set if given a new valid constant value.''' # Test with valid constant values sym = DataSymbol('a', INTEGER_SINGLE_TYPE, constant_value=7) assert sym.constant_value.value == "7" sym.constant_value = 9 assert sym.constant_value.value == "9" sym = DataSymbol('a', REAL_SINGLE_TYPE, constant_value=3.1415) assert sym.constant_value.value == "3.1415" sym.constant_value = 1.0 assert sym.constant_value.value == "1.0" sym = DataSymbol('a', BOOLEAN_TYPE, constant_value=True) assert sym.constant_value.value == "true" sym.constant_value = False assert sym.constant_value.value == "false" # Test with valid constant expressions lhs = Literal('2', INTEGER_SINGLE_TYPE) rhs = Reference(DataSymbol('constval', INTEGER_SINGLE_TYPE)) ct_expr = BinaryOperation.create(BinaryOperation.Operator.ADD, lhs, rhs) sym = DataSymbol('a', INTEGER_SINGLE_TYPE, constant_value=ct_expr) assert isinstance(sym.constant_value, BinaryOperation) assert sym.constant_value is ct_expr
def test_type_convert_binaryop_create(operation, op_str): '''Test that the create method in the BinaryOperation class correctly creates a BinaryOperation instance for the REAL and INT type-conversion operations.. ''' sym = DataSymbol("tmp1", REAL_SINGLE_TYPE) lhs = Reference(sym) wp_sym = DataSymbol("wp", INTEGER_SINGLE_TYPE) # Reference to a kind parameter rhs = Reference(wp_sym) binaryoperation = BinaryOperation.create(operation, lhs, rhs) assert binaryoperation._operator is operation check_links(binaryoperation, [lhs, rhs]) result = FortranWriter().binaryoperation_node(binaryoperation) assert op_str + "(tmp1, wp)" in result.lower() # Kind specified with an integer literal rhs = Literal("4", INTEGER_SINGLE_TYPE) binaryoperation = BinaryOperation.create(operation, lhs.detach(), rhs) check_links(binaryoperation, [lhs, rhs]) result = FortranWriter().binaryoperation_node(binaryoperation) assert op_str + "(tmp1, 4)" in result.lower() # Kind specified as an arithmetic expression rhs = BinaryOperation.create(BinaryOperation.Operator.ADD, Reference(wp_sym), Literal("2", INTEGER_SINGLE_TYPE)) binaryoperation = BinaryOperation.create(operation, lhs.detach(), rhs) check_links(binaryoperation, [lhs, rhs]) result = FortranWriter().binaryoperation_node(binaryoperation) assert op_str + "(tmp1, wp + 2)" in result.lower()
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_cw_assignment(): '''Check the CWriter class assignment method generate the appropriate output. ''' assignment = Assignment.create(Reference(DataSymbol('a', REAL_TYPE)), Reference(DataSymbol('b', REAL_TYPE))) # Generate C from the PSyIR schedule cwriter = CWriter() result = cwriter(assignment) assert result == "a = b;\n"
def test_cw_binaryoperator(): '''Check the CWriter class binary_operation method correctly prints out the C representation of any given BinaryOperation. ''' cwriter = CWriter() # Test UnaryOperation without children. binary_operation = BinaryOperation(BinaryOperation.Operator.ADD) with pytest.raises(VisitorError) as err: _ = cwriter(binary_operation) assert ("BinaryOperation malformed or incomplete. It should have " "exactly 2 children, but found 0." in str(err.value)) # Test with children ref1 = Reference(DataSymbol("a", REAL_TYPE)) ref2 = Reference(DataSymbol("b", REAL_TYPE)) binary_operation = BinaryOperation.create(BinaryOperation.Operator.ADD, ref1, ref2) assert cwriter(binary_operation) == '(a + b)' # Test all supported Operators test_list = ((BinaryOperation.Operator.ADD, '(a + b)'), (BinaryOperation.Operator.SUB, '(a - b)'), (BinaryOperation.Operator.MUL, '(a * b)'), (BinaryOperation.Operator.DIV, '(a / b)'), (BinaryOperation.Operator.REM, '(a % b)'), (BinaryOperation.Operator.POW, 'pow(a, b)'), (BinaryOperation.Operator.EQ, '(a == b)'), (BinaryOperation.Operator.NE, '(a != b)'), (BinaryOperation.Operator.GT, '(a > b)'), (BinaryOperation.Operator.GE, '(a >= b)'), (BinaryOperation.Operator.LT, '(a < b)'), (BinaryOperation.Operator.LE, '(a <= b)'), (BinaryOperation.Operator.AND, '(a && b)'), (BinaryOperation.Operator.OR, '(a || b)'), (BinaryOperation.Operator.SIGN, 'copysign(a, b)')) for operator, expected in test_list: binary_operation._operator = operator assert cwriter(binary_operation) == expected # Test that an unsupported operator raises a error class Unsupported(object): '''Dummy class''' def __init__(self): pass binary_operation._operator = Unsupported with pytest.raises(VisitorError) as err: _ = cwriter(binary_operation) assert "The C backend does not support the '" in str(err.value) assert "' operator." in str(err.value)
def test_binaryoperation_create(): '''Test that the create method in the BinaryOperation class correctly creates a BinaryOperation instance. ''' lhs = Reference(DataSymbol("tmp1", REAL_SINGLE_TYPE)) rhs = Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)) oper = BinaryOperation.Operator.ADD binaryoperation = BinaryOperation.create(oper, lhs, rhs) check_links(binaryoperation, [lhs, rhs]) result = FortranWriter().binaryoperation_node(binaryoperation) assert result == "tmp1 + tmp2"
def test_naryoperation_create(): '''Test that the create method in the NaryOperation class correctly creates an NaryOperation instance. ''' children = [Reference(DataSymbol("tmp1", REAL_SINGLE_TYPE)), Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Reference(DataSymbol("tmp3", REAL_SINGLE_TYPE))] oper = NaryOperation.Operator.MAX naryoperation = NaryOperation.create(oper, children) check_links(naryoperation, children) result = FortranWriter().naryoperation_node(naryoperation) assert result == "MAX(tmp1, tmp2, tmp3)"
def test_create_unknown(): '''Test that the static create() method creates a NemoLoop instance with an unknown loop type if the loop type is not recognised. ''' variable = DataSymbol("idx", INTEGER_TYPE) start = Literal("2", INTEGER_TYPE) stop = Literal("10", INTEGER_TYPE) step = Literal("1", INTEGER_TYPE) x_var = DataSymbol("X", REAL_TYPE) children = [Assignment.create(Reference(x_var), Literal("3.0", REAL_TYPE))] nemo_loop = NemoLoop.create(variable, start, stop, step, children) assert nemo_loop.loop_type == "unknown"
def test_call_create(): '''Test that the create method creates a valid call with arguments''' routine = RoutineSymbol("ellie") array_type = ArrayType(INTEGER_TYPE, shape=[10, 20]) arguments = [ Reference(DataSymbol("arg1", INTEGER_TYPE)), Array(DataSymbol("arg2", array_type)) ] call = Call.create(routine, arguments) assert call.routine is routine for idx, child, in enumerate(call.children): assert child is arguments[idx] assert child.parent is call
def test_cw_size(): ''' Check the CWriter class SIZE method raises the expected error since there is no C equivalent. ''' cwriter = CWriter() arr = ArrayReference(DataSymbol('a', INTEGER_TYPE)) lit = Literal('1', INTEGER_TYPE) size = BinaryOperation.create(BinaryOperation.Operator.SIZE, arr, lit) lhs = Reference(DataSymbol('length', INTEGER_TYPE)) assignment = Assignment.create(lhs, size) with pytest.raises(VisitorError) as excinfo: cwriter(assignment) assert ("C backend does not support the 'Operator.SIZE' operator" in str(excinfo.value))
def test_datasymbol_scalar_array(): '''Test that the DataSymbol property is_scalar returns True if the DataSymbol is a scalar and False if not and that the DataSymbol property is_array returns True if the DataSymbol is an array and False if not. ''' sym1 = DataSymbol("s1", INTEGER_SINGLE_TYPE) array_type = ArrayType(REAL_SINGLE_TYPE, [ArrayType.Extent.ATTRIBUTE, 2, sym1]) sym2 = DataSymbol("s2", array_type) assert sym1.is_scalar assert not sym1.is_array assert not sym2.is_scalar assert sym2.is_array
def test_ifblock_create_invalid(): '''Test that the create method in an IfBlock class raises the expected exception if the provided input is invalid. ''' if_condition = Literal('true', BOOLEAN_TYPE) if_body = [ Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), Assignment.create(Reference(DataSymbol("tmp2", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE)) ] # if_condition not a Node. with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create("True", if_body) assert ("Item 'str' can't be child 0 of 'If'. The valid format is: " "'DataNode, Schedule [, Schedule]'.") in str(excinfo.value) # One or more if body not a Node. if_body_err = [ Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("0.0", REAL_SINGLE_TYPE)), "invalid" ] with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, if_body_err) assert ("Item 'str' can't be child 1 of 'Schedule'. The valid format is: " "'[Statement]*'.") in str(excinfo.value) # If body not a list. with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, "invalid") assert ("if_body argument in create method of IfBlock class should be a " "list.") in str(excinfo.value) # One of more of else_body not a Node. else_body_err = [ Assignment.create(Reference(DataSymbol("tmp", REAL_SINGLE_TYPE)), Literal("1.0", REAL_SINGLE_TYPE)), "invalid" ] with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, if_body, else_body_err) assert ("Item 'str' can't be child 1 of 'Schedule'. The valid format is: " "'[Statement]*'.") in str(excinfo.value) # Else body not a list. with pytest.raises(GenerationError) as excinfo: _ = IfBlock.create(if_condition, if_body, "invalid") assert ("else_body argument in create method of IfBlock class should be a " "list.") in str(excinfo.value)