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_reference_accesses_bounds(operator_type): '''Test that the reference_accesses method behaves as expected when the reference is the first argument to either the lbound or ubound intrinsic as that is simply looking up the array bounds (therefore var_access_info should be empty) and when the reference is the second argument of either the lbound or ubound intrinsic (in which case the access should be a read). ''' # Note, one would usually expect UBOUND to provide the upper bound # of a range but to simplify the test both LBOUND and UBOUND are # used for the lower bound. This does not affect the test. one = Literal("1", INTEGER_TYPE) array_symbol = DataSymbol("test", ArrayType(REAL_TYPE, [10])) array_ref1 = Reference(array_symbol) array_ref2 = Reference(array_symbol) array_access = Array.create(array_symbol, [one]) # test when first or second argument to LBOUND or UBOUND is an # array reference operator = BinaryOperation.create(operator_type, array_ref1, array_ref2) array_access.children[0] = Range.create(operator, one, one) var_access_info = VariablesAccessInfo() array_ref1.reference_accesses(var_access_info) assert str(var_access_info) == "" var_access_info = VariablesAccessInfo() array_ref2.reference_accesses(var_access_info) assert str(var_access_info) == "test: READ"
def test_range_view(capsys): ''' Check that calling view() on an array with a child Range works as expected. ''' from psyclone.psyir.nodes import Array from psyclone.psyir.nodes.node import colored, SCHEDULE_COLOUR_MAP # Create the PSyIR for 'my_array(1, 1:10)' erange = Range.create(Literal("1", INTEGER_SINGLE_TYPE), Literal("10", INTEGER_SINGLE_TYPE)) array_type = ArrayType(REAL_SINGLE_TYPE, [10, 10]) array = Array.create(DataSymbol("my_array", array_type), [Literal("1", INTEGER_SINGLE_TYPE), erange]) array.view() stdout, _ = capsys.readouterr() arrayref = colored("ArrayReference", SCHEDULE_COLOUR_MAP[array._colour_key]) literal = colored("Literal", SCHEDULE_COLOUR_MAP[array.children[0]._colour_key]) rangestr = colored("Range", SCHEDULE_COLOUR_MAP[erange._colour_key]) indent = " " assert (arrayref + "[name:'my_array']\n" + indent + literal + "[value:'1', Scalar<INTEGER, SINGLE>]\n" + indent + rangestr + "[]\n" + 2 * indent + literal + "[value:'1', Scalar<INTEGER, SINGLE>]\n" + 2 * indent + literal + "[value:'10', Scalar<INTEGER, SINGLE>]\n" + 2 * indent + literal + "[value:'1', Scalar<INTEGER, UNDEFINED>]\n" in stdout)
def test_array_create_invalid3(): '''Test that the create method in an Array class raises the expected exception if the provided input is invalid. ''' # symbol argument is not a DataSymbol with pytest.raises(GenerationError) as excinfo: _ = Array.create([], []) assert ("symbol argument in create method of Array class should " "be a DataSymbol but found 'list'." in str(excinfo.value)) # children not a list with pytest.raises(GenerationError) as excinfo: _ = Array.create(DataSymbol("temp", REAL_SINGLE_TYPE), "invalid") assert ("children argument in create method of Array class should " "be a list but found 'str'." in str(excinfo.value))
def create_matmul(): '''Utility function that creates a valid matmul node for use with subsequent tests. ''' symbol_table = SymbolTable() one = Literal("1", INTEGER_TYPE) two = Literal("2", INTEGER_TYPE) index = DataSymbol("idx", INTEGER_TYPE, constant_value=3) symbol_table.add(index) array_type = ArrayType(REAL_TYPE, [5, 10, 15]) mat_symbol = DataSymbol("x", array_type) symbol_table.add(mat_symbol) lbound1 = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(mat_symbol), one) ubound1 = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(mat_symbol), one) my_mat_range1 = Range.create(lbound1, ubound1, one) lbound2 = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(mat_symbol), two) ubound2 = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(mat_symbol), two) my_mat_range2 = Range.create(lbound2, ubound2, one) matrix = Array.create(mat_symbol, [my_mat_range1, my_mat_range2, Reference(index)]) array_type = ArrayType(REAL_TYPE, [10, 20]) vec_symbol = DataSymbol("y", array_type) symbol_table.add(vec_symbol) lbound = BinaryOperation.create(BinaryOperation.Operator.LBOUND, Reference(vec_symbol), one) ubound = BinaryOperation.create(BinaryOperation.Operator.UBOUND, Reference(vec_symbol), one) my_vec_range = Range.create(lbound, ubound, one) vector = Array.create(vec_symbol, [my_vec_range, Reference(index)]) matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, matrix, vector) lhs_type = ArrayType(REAL_TYPE, [10]) lhs_symbol = DataSymbol("result", lhs_type) symbol_table.add(lhs_symbol) lhs = Reference(lhs_symbol) assign = Assignment.create(lhs, matmul) KernelSchedule.create("my_kern", symbol_table, [assign]) return matmul
def test_array_is_upper_bound(): '''Test that the is_upper_bound method in the Array Node works as expected. ''' one = Literal("1", INTEGER_TYPE) array = Array.create(DataSymbol("test", ArrayType(REAL_TYPE, [10])), [one]) with pytest.raises(TypeError) as info: array.is_upper_bound("hello") assert ("The index argument should be an integer but found 'str'." in str(info.value)) # not a range node at index 0 assert not array.is_upper_bound(0) # range node does not have a binary operator for its stop value array.children[0] = Range.create(one, one, one) assert not array.is_upper_bound(0) # range node ubound references a different array array2 = Array.create(DataSymbol("test2", ArrayType(REAL_TYPE, [10])), [one]) operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND, array2, one) array.children[0] = Range.create(one, operator, one) assert not array.is_upper_bound(0) # range node ubound references a different index operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND, array, Literal("2", INTEGER_TYPE)) array.children[0] = Range.create(one, operator, one) assert not array.is_upper_bound(0) # all is well operator = BinaryOperation.create(BinaryOperation.Operator.UBOUND, array, one) array.children[0] = Range.create(one, operator, one) assert array.is_upper_bound(0)
def test_array_validate_index(): '''Test that the validate_index utility function behaves as expected.''' array = Array.create(DataSymbol("test", ArrayType(REAL_TYPE, [10])), [Literal("1", INTEGER_TYPE)]) with pytest.raises(TypeError) as info: array._validate_index("hello") assert ("The index argument should be an integer but found 'str'." in str(info.value)) with pytest.raises(ValueError) as info: array._validate_index(1) assert ("In Array 'test' the specified index '1' must be less than the " "number of dimensions '1'." in str(info.value)) array._validate_index(0) array._validate_index(-1)
def test_array_create_invalid1(): '''Test that the create method in the Array class raises an exception if the provided symbol is not an array. ''' symbol_i = DataSymbol("i", INTEGER_SINGLE_TYPE) symbol_j = DataSymbol("j", INTEGER_SINGLE_TYPE) symbol_temp = DataSymbol("temp", REAL_SINGLE_TYPE) children = [ Reference(symbol_i), Reference(symbol_j), Literal("1", INTEGER_SINGLE_TYPE) ] with pytest.raises(GenerationError) as excinfo: _ = Array.create(symbol_temp, children) assert ("expecting the symbol to be an array, not a scalar." in str(excinfo.value))
def test_validate(): '''Test that the validate method in the ArrayRange2LoopTrans class raises the expected exceptions. ''' trans = ArrayRange2LoopTrans() with pytest.raises(TransformationError) as info: trans.validate(Node()) assert ("Error in ArrayRange2LoopTrans transformation. The supplied node " "argument should be a PSyIR Assignment, but found 'Node'." in str(info.value)) with pytest.raises(TransformationError) as info: trans.validate(Assignment.create(DataNode(), DataNode())) assert ("Error in ArrayRange2LoopTrans transformation. The lhs of the " "supplied Assignment node should be a PSyIR Array, but found " "'DataNode'." in str(info.value)) array_symbol = DataSymbol("x", ArrayType(INTEGER_TYPE, [10, 10])) one = Literal("1", INTEGER_TYPE) array_assignment = Array.create(array_symbol, [one, one]) with pytest.raises(TransformationError) as info: trans.validate(Assignment.create(array_assignment, DataNode())) assert ( "Error in ArrayRange2LoopTrans transformation. The lhs of the " "supplied Assignment node should be a PSyIR Array with at least one " "of its dimensions being a Range, but found None in " "'ArrayArrayReference[name:'x']\\nLiteral[value:'1', Scalar<INTEGER, " "UNDEFINED>]\\nLiteral[value:'1', Scalar<INTEGER, UNDEFINED>]\\n'." in str(info.value)) array_x = create_array_x(SymbolTable()) assignment = Assignment.create(create_array_x(SymbolTable()), array_x) trans.validate(assignment) array_x.children[0].step = Literal("2", INTEGER_TYPE) with pytest.raises(TransformationError) as info: trans.validate(assignment) assert ( "The ArrayRange2LoopTrans transformation only supports ranges that " "are known to be the same as each other but array access 'x' " "dimension 0 and 'x' dimension 0 are either different or can't be " "determined in the assignment 'Assignment[]\\nArrayArrayReference[" "name:'x']\\nRange[]\\nArrayArrayReference[name:'x']\\nRange[]\\n'." in str(info.value))
def test_reference(): '''Test that the common reference visitor writes the referenced symbol name or raises an error if the node is not Leaf node reference. ''' # Generate PSyIR Reference test_visitor = PSyIRVisitor() reference = Reference(DataSymbol('a', REAL_TYPE)) result = test_visitor(reference) assert result == "a" # Generate PSyIR Array reference2 = Array.create(DataSymbol('b', ArrayType(REAL_TYPE, shape=[1])), [reference]) with pytest.raises(VisitorError) as err: result = test_visitor(reference2) assert "Expecting a Reference with no children but found: " \ in str(err.value)
def create_array_x(symbol_table): '''Utility routine that creates and returns an array reference to a one-dimensional array "x". The array reference accesses all of the elements in the array dimension using a range node. In Fortran array notation this is "x(:)". :param symbol_table: the symbol table to which we add the array \ symbol. :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable` :returns: an array reference that accesses all elements of the \ array "x". :rtype: :py:class:`psyclone.psyir.nodes.Array` ''' array_symbol = DataSymbol("x", ArrayType(REAL_TYPE, [10])) symbol_table.add(array_symbol) return Array.create(array_symbol, [create_range(array_symbol, 1)])
def test_array_create(): '''Test that the create method in the Array class correctly creates an Array instance. ''' array_type = ArrayType(REAL_SINGLE_TYPE, [10, 10, 10]) symbol_temp = DataSymbol("temp", array_type) symbol_i = DataSymbol("i", INTEGER_SINGLE_TYPE) symbol_j = DataSymbol("j", INTEGER_SINGLE_TYPE) children = [ Reference(symbol_i), Reference(symbol_j), Literal("1", INTEGER_SINGLE_TYPE) ] array = Array.create(symbol_temp, children) check_links(array, children) result = FortranWriter().array_node(array) assert result == "temp(i,j,1)"
def test_validate5(): '''Check that the Matmul2Code validate method raises the expected exception when the supplied node is a MATMUL binary operation but either or both arguments are not references. ''' trans = Matmul2CodeTrans() array_type = ArrayType(REAL_TYPE, [10]) array = Array.create(DataSymbol("x", array_type), [Literal("10", INTEGER_TYPE)]) mult = BinaryOperation.create(BinaryOperation.Operator.MUL, array, array) matmul = BinaryOperation.create(BinaryOperation.Operator.MATMUL, mult, mult) _ = Assignment.create(array, matmul) with pytest.raises(TransformationError) as excinfo: trans.validate(matmul) assert ("Expected children of a MATMUL BinaryOperation to be references, " "but found 'BinaryOperation', 'BinaryOperation'." in str(excinfo.value))
def test_array_create_invalid2(): '''Test that the create method in the Array class raises an exception if the number of dimension in the provided symbol is different to the number of indices provided to the create method. ''' array_type = ArrayType(REAL_SINGLE_TYPE, [10]) symbol_temp = DataSymbol("temp", array_type) symbol_i = DataSymbol("i", INTEGER_SINGLE_TYPE) symbol_j = DataSymbol("j", INTEGER_SINGLE_TYPE) children = [ Reference(symbol_i), Reference(symbol_j), Literal("1", INTEGER_SINGLE_TYPE) ] with pytest.raises(GenerationError) as excinfo: _ = Array.create(symbol_temp, children) assert ("the symbol should have the same number of dimensions as indices " "(provided in the 'children' argument). Expecting '3' but found " "'1'." in str(excinfo.value))
def create_array_y_2d_slice(symbol_table): '''Utility routine that creates and returns an array reference to a 2 dimensional array "y". The array reference accesses all elements in the 1st and 2nd dimensions of the array using range nodes. In Fortran array notation this is "y(:,:)". :param symbol_table: the symbol table to which we add the array \ symbol. :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable` :returns: an array reference that accesses all elements of the 1st \ and 2nd dimensions of the "y" array. :rtype: :py:class:`psyclone.psyir.nodes.Array` ''' array_symbol = DataSymbol("y2", ArrayType(REAL_TYPE, [20, 10])) symbol_table.add(array_symbol) return Array.create( array_symbol, [create_range(array_symbol, 1), create_range(array_symbol, 2)])
def test_where_array_notation_rank(): ''' Test that the _array_notation_rank() utility raises the expected errors when passed an unsupported Array object. ''' array_type = ArrayType(REAL_TYPE, [10]) symbol = DataSymbol("my_array", array_type) my_array = Array(symbol) processor = Fparser2Reader() with pytest.raises(NotImplementedError) as err: processor._array_notation_rank(my_array) assert ("Array reference in the PSyIR must have at least one child but " "'my_array'" in str(err.value)) from psyclone.psyir.nodes import Range array_type = ArrayType(REAL_TYPE, [10]) my_array = Array.create(DataSymbol("my_array", array_type), [Range.create(Literal("1", INTEGER_TYPE), Literal("10", INTEGER_TYPE))]) with pytest.raises(NotImplementedError) as err: processor._array_notation_rank(my_array) assert ("Only array notation of the form my_array(:, :, ...) is " "supported." in str(err.value))
def create_array_y_slice_subset(symbol_table): '''Utility routine that creates and returns an array reference to a 2 dimensional array "y". The array reference accesses elements 2 to "n" step 2 in the arrays 2nd dimension using a range node and the n'th element of the 1st dimension. In Fortran array notation this is "y(n,2:n:2)". :param symbol_table: the symbol table to which we add the array \ symbol and access the symbol "n". :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable` :returns: an array reference that accesses elements 2 to "n" step \ 2 in the array "y"'s 2nd dimension and the n'th element of its \ 1st dimension. :rtype: :py:class:`psyclone.psyir.nodes.Array` ''' array_symbol = DataSymbol("y3", ArrayType(REAL_TYPE, [10, 10])) symbol_table.add(array_symbol) symbol_n = symbol_table.lookup("n") return Array.create(array_symbol, [Reference(symbol_n), create_stepped_range(symbol_n)])
def create_array_y(symbol_table): '''Utility routine that creates and returns an array reference to a two-dimensional array "y". The array reference accesses all elements in the 2nd dimension of the array using a range node and the n'th element of the 1st dimension. In Fortran array notation this is "y(n,:)". :param symbol_table: the symbol table to which we add the array \ symbol and access the symbol "n". :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable` :returns: an array reference that accesses all elements of the \ 2nd dimension of array "y" and the n'th element of its 1st \ dimension. :rtype: :py:class:`psyclone.psyir.nodes.Array` ''' array_symbol = DataSymbol("y", ArrayType(REAL_TYPE, [10, 10])) symbol_table.add(array_symbol) return Array.create( array_symbol, [Reference(symbol_table.lookup("n")), create_range(array_symbol, 2)])
def create_array_z(symbol_table): '''Utility routine that creates and returns an array reference to a 3 dimensional array "z". The array reference accesses all elements in the arrays 1st and 3rd dimensions using range nodes and accesses element "n" in its second dimension. In Fortran array notation this is "z(:,n,:)". :param symbol_table: the symbol table to which we add the array \ symbol and access the symbol "n" :type symbol_table: :py:class:`psyclone.psyir.symbol.SymbolTable` :returns: an array reference that accesses all elements of the 1st \ and 3rd dimensions of array "z" and the n'th element of its \ second dimension. :rtype: :py:class:`psyclone.psyir.nodes.Array` ''' array_symbol = DataSymbol("z", ArrayType(REAL_TYPE, [20, 10, 10])) symbol_table.add(array_symbol) return Array.create(array_symbol, [ create_range(array_symbol, 1), Reference(symbol_table.lookup("n")), create_range(array_symbol, 3) ])
def apply(self, node, options=None): '''Apply the MATMUL intrinsic conversion transformation to the specified node. This node must be a MATMUL BinaryOperation. Currently only the matrix vector version of MATMUL is supported. The arguments are permitted to have additional dimensions (i.e. more than 2 for the matrix and more than 1 for the vector) but the matrix can only have two indices which are ranges and these must be the first two indices and the vector can only have one index that is a range and this must be the first index. Further, the ranges must be for the full index space for that dimension (i.e. array subsections are not supported). If the transformation is successful then an assignment which includes a MATMUL BinaryOperation node is converted to equivalent inline code. :param node: a MATMUL Binary-Operation node. :type node: :py:class:`psyclone.psyGen.BinaryOperation` :param options: a dictionary with options for transformations. :type options: dictionary of string:values or None ''' # pylint: disable=too-many-locals self.validate(node) assignment = node.parent matrix = node.children[0] vector = node.children[1] result = node.parent.lhs result_symbol = result.symbol # Create new i and j loop iterators. symbol_table = node.scope.symbol_table i_loop_name = symbol_table.new_symbol_name("i") i_loop_symbol = DataSymbol(i_loop_name, INTEGER_TYPE) symbol_table.add(i_loop_symbol) j_loop_name = symbol_table.new_symbol_name("j") j_loop_symbol = DataSymbol(j_loop_name, INTEGER_TYPE) symbol_table.add(j_loop_symbol) # Create "result(i)" result_dims = [Reference(i_loop_symbol)] if len(result.children) > 1: # Add any additional dimensions (in case of an array slice) result_dims.extend(result.children[1:]) result = Array.create(result_symbol, result_dims) # Create "vector(j)" vector_dims = [Reference(j_loop_symbol)] if len(vector.children) > 1: # Add any additional dimensions (in case of an array slice) vector_dims.extend(vector.children[1:]) vector_array_reference = Array.create(vector.symbol, vector_dims) # Create "matrix(i,j)" array_dims = [Reference(i_loop_symbol), Reference(j_loop_symbol)] if len(matrix.children) > 2: # Add any additional dimensions (in case of an array slice) array_dims.extend(matrix.children[2:]) matrix_array_reference = Array.create(matrix.symbol, array_dims) # Create "matrix(i,j) * vector(j)" multiply = BinaryOperation.create(BinaryOperation.Operator.MUL, matrix_array_reference, vector_array_reference) # Create "result(i) + matrix(i,j) * vector(j)" rhs = BinaryOperation.create(BinaryOperation.Operator.ADD, result, multiply) # Create "result(i) = result(i) + matrix(i,j) * vector(j)" assign = Assignment.create(result, rhs) # Create j loop and add the above code as a child # Work out the bounds lower_bound, upper_bound, step = _get_array_bound(vector, 0) jloop = Loop.create(j_loop_symbol, lower_bound, upper_bound, step, [assign]) # Create "result(i) = 0.0" assign = Assignment.create(result, Literal("0.0", REAL_TYPE)) # Create i loop and add assigment and j loop as children lower_bound, upper_bound, step = _get_array_bound(matrix, 0) iloop = Loop.create(i_loop_symbol, lower_bound, upper_bound, step, [assign, jloop]) # Add the new code to the PSyIR iloop.parent = assignment.parent assignment.parent.children.insert(assignment.position, iloop) # remove the original matmul assignment.parent.children.remove(assignment)
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_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
# 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 = Array.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]) # If statement IF_CONDITION = BinaryOperation.create(BinaryOperation.Operator.GT, TMP1, ZERO) IFBLOCK = IfBlock.create(IF_CONDITION, [ASSIGN3, ASSIGN4])