Permalink
Browse files

Support non-slice array expression assignment

  • Loading branch information...
1 parent d640297 commit 62f8783e658b5d18f98203a70cc3f0bb7b7f500e @markflorisson committed Jun 23, 2012
View
75 Cython/Compiler/ExprNodes.py
@@ -7587,6 +7587,34 @@ def generate_result_code(self, code):
self.operand.result()))
code.put_incref(self.result(), self.ctype())
+class FormatStringNode(ExprNode):
+ """
+ Create a format string for a cython.view.array.
+
+ Attributes:
+ dtype
+ """
+
+ subexprs = []
+
+ def analyse_types(self, env):
+ self.type = py_object_type
+ self.is_temp = True
+ load_cython_array_utilities(env)
+
+ def generate_result_code(self, code):
+ import Buffer
+ type_info = Buffer.get_type_information_cname(code, self.dtype)
+ code.putln("%s = __pyx_format_from_typeinfo(&%s);" %
+ (self.result(), type_info))
+ err = "!%s || !PyBytes_AsString(%s)" % (self.result(), self.result())
+ code.putln(code.error_goto_if(err, self.pos))
+ code.put_gotref(self.result())
+
+def load_cython_array_utilities(env):
+ import MemoryView
+ MemoryView.use_cython_array_utility_code(env)
+ env.use_utility_code(MemoryView.typeinfo_to_format_code)
ERR_START = "Start may not be given"
ERR_NOT_STOP = "Stop must be provided to indicate shape"
@@ -7616,9 +7644,10 @@ class CythonArrayNode(ExprNode):
base_type_node MemoryViewSliceTypeNode the cast expression node
"""
- subexprs = ['operand', 'shapes']
+ subexprs = ['format_string', 'operand', 'shapes']
shapes = None
+ format_string = None
is_temp = True
mode = "c"
array_dtype = None
@@ -7663,6 +7692,9 @@ def analyse_types(self, env):
"Expected %d dimensions, array has %d dimensions" %
(ndim, len(array_dimension_sizes)))
+ self.format_string = FormatStringNode(self.pos, dtype=array_dtype)
+ self.format_string.analyse_types(env)
+
# Verify the start, stop and step values
# In case of a C array, use the size of C array in each dimension to
# get an automatic cast
@@ -7716,8 +7748,7 @@ def analyse_types(self, env):
self.coercion_type = PyrexTypes.MemoryViewSliceType(array_dtype, axes)
self.coercion_type.validate_memslice_dtype(self.pos)
self.type = self.get_cython_array_type(env)
- MemoryView.use_cython_array_utility_code(env)
- env.use_utility_code(MemoryView.typeinfo_to_format_code)
+ load_cython_array_utilities(env)
def allocate_temp_result(self, code):
if self.temp_code:
@@ -7740,34 +7771,25 @@ def generate_result_code(self, code):
for shape in self.shapes]
dtype = self.coercion_type.dtype
- shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
- format_temp = code.funcstate.allocate_temp(py_object_type, True)
-
- itemsize = "sizeof(%s)" % dtype.declaration_code("")
- type_info = Buffer.get_type_information_cname(code, dtype)
-
if self.operand.type.is_ptr:
code.putln("if (!%s) {" % self.operand.result())
code.putln( 'PyErr_SetString(PyExc_ValueError,'
'"Cannot create cython.array from NULL pointer");')
code.putln(code.error_goto(self.operand.pos))
code.putln("}")
- code.putln("%s = __pyx_format_from_typeinfo(&%s);" %
- (format_temp, type_info))
+ shapes_temp = code.funcstate.allocate_temp(py_object_type, True)
buildvalue_fmt = " __PYX_BUILD_PY_SSIZE_T " * len(shapes)
code.putln('%s = Py_BuildValue("(" %s ")", %s);' % (shapes_temp,
buildvalue_fmt,
", ".join(shapes)))
- err = "!%s || !%s || !PyBytes_AsString(%s)" % (format_temp,
- shapes_temp,
- format_temp)
- code.putln(code.error_goto_if(err, self.pos))
- code.put_gotref(format_temp)
+ code.putln(code.error_goto_if("!%s" % shapes_temp, self.pos))
code.put_gotref(shapes_temp)
- tup = (self.result(), shapes_temp, itemsize, format_temp,
+ tup = (self.result(), shapes_temp,
+ "sizeof(%s)" % dtype.declaration_code(""),
+ self.format_string.result(),
self.mode, self.operand.result())
code.putln('%s = __pyx_array_new('
'%s, %s, PyBytes_AS_STRING(%s), '
@@ -7780,7 +7802,6 @@ def dispose(temp):
code.funcstate.release_temp(temp)
dispose(shapes_temp)
- dispose(format_temp)
@classmethod
def from_carray(cls, src_node, env):
@@ -9622,6 +9643,7 @@ def __init__(self, arg, env, type=py_object_type):
if self.arg.type.is_memoryviewslice:
# register utility codes for conversion to/from the memoryview dtype
self.arg.type.dtype_object_conversion_funcs(env)
+ self.is_elemental = self.arg.is_elemental
self.env = env
@@ -9872,13 +9894,13 @@ class ProxyNode(CoercionNode):
def __init__(self, arg):
super(ProxyNode, self).__init__(arg)
- self._proxy_type()
+ self.proxy_type()
- def analyse_expressions(self, env):
- self.arg.analyse_expressions(env)
- self._proxy_type()
+ def analyse_types(self, env):
+ self.arg.analyse_types(env)
+ self.proxy_type()
- def _proxy_type(self):
+ def proxy_type(self):
if hasattr(self.arg, 'type'):
self.type = self.arg.type
self.result_ctype = self.arg.result_ctype
@@ -9909,6 +9931,10 @@ def generate_disposal_code(self, code):
def free_temps(self, code):
self.arg.free_temps(code)
+ def generate_assignment_code(self, rhs, code):
+ self.arg.generate_assignment_code(rhs, code)
+
+
class CloneNode(CoercionNode):
# This node is employed when the result of another node needs
# to be used multiple times. The argument node's result must
@@ -9949,6 +9975,9 @@ def analyse_types(self, env):
if hasattr(self.arg, 'entry'):
self.entry = self.arg.entry
+ def result_in_temp(self):
+ return True
+
def is_simple(self):
return True # result is always in a temp (or a name)
View
2 Cython/Compiler/MemoryView.py
@@ -71,7 +71,7 @@ def put_init_entry(mv_cname, code):
def put_acquire_memoryviewslice(lhs_cname, lhs_type, lhs_pos, rhs, code,
have_gil=False, first_assignment=True):
"We can avoid decreffing the lhs if we know it is the first assignment"
- assert rhs.type.is_memoryviewslice
+ assert rhs.type.is_memoryviewslice, rhs.type
pretty_rhs = rhs.result_in_temp() or rhs.is_simple()
if pretty_rhs:
View
279 Cython/Compiler/Vector.py
@@ -353,15 +353,49 @@ def generate_result_code(self, code):
pass
class TempSliceStruct(ExprNodes.ExprNode):
+ """
+ Alloate a temporary memoryview slice (only the struct temporary).
+ """
subexprs = []
def analyse_types(self, env):
self.type = PyrexTypes.MemoryViewSliceType(
self.dtype, self.axes[-self.ndim:])
self.is_temp = True
+ def generate_assignment_code(self, rhs, code):
+ code.put_incref_memoryviewslice(rhs.result(),
+ have_gil=not self.in_nogil_context)
+ code.putln("%s = %s;" % (self.result(), rhs.result()))
+ rhs.generate_disposal_code(code)
+ rhs.free_temps(code)
+
def generate_result_code(self, code):
pass
+class TempCythonArrayNode(ExprNodes.ExprNode):
+ """
+ Attributes:
+ dest_array_type
+ rhs: the broadcasted rhs node
+ """
+ subexprs = ['format_string']
+ def analyse_types(self, env):
+ self.dtype = self.dest_array_type.dtype
+ self.type = PyrexTypes.py_object_type
+ self.format_string = ExprNodes.FormatStringNode(
+ self.pos, dtype=self.dtype)
+ self.format_string.analyse_types(env)
+ self.is_temp = True
+
+ def generate_result_code(self, code):
+ t = (self.result(), self.rhs.result(), self.rhs.type.ndim,
+ self.dtype.declaration_code(""), self.format_string.result(),
+ MemoryView.get_best_slice_order(self.rhs))
+ code.putln("%s = (PyObject *) __pyx_array_new_simple("
+ "&%s.shape[0], %d, sizeof(%s), %s, %s);" % t)
+ code.putln(code.error_goto_if_null(self.result(), self.pos))
+ code.put_gotref(self.result())
+
class DetermineArrayLayoutNode(ExprNodes.ExprNode):
subexprs = []
def analyse_types(self, env):
@@ -649,27 +683,43 @@ class ElementalNode(Nodes.StatNode):
read
2) Some error may occur while evaluating the rhs
- rhs: entire RHS expression node
- sources: list of broadcastable array operands, excluding the LHS
- may_error: indicates whether the expression may raise a sudden error
+ Attributes:
+ rhs: entire RHS expression node
+ sources: list of broadcastable array operands, excluding the LHS
+ may_error: indicates whether the expression may raise a sudden error
+
+ For expressions like 'c = a + b', i.e., in case of no slice assignment
+ acquire_slice:
+ This assignment creates a cython.view.array and acquires a
+ memoryview slice from that in self.lhs (a temporary)
+ final_lhs_assignment:
+ Assignment of temporary memoryview slice of a
+ cython.view.array to the final lhs ('c').
"""
child_attrs = ['operands', 'scalar_operands', 'temp_nodes', 'lhs',
'check_overlap', 'rhs', 'final_assignment_node',
- 'broadcast', 'final_broadcast', 'temp_dst']
+ 'broadcast', 'final_broadcast', 'temp_dst',
+ 'acquire_slice', 'final_lhs_assignment']
check_overlap = None
+ temp_dst = None
may_error = None
rhs = None
rhs_target = None
+ final_broadcast = None
+ final_assignment_node = None
+
+ acquire_slice = None
+ final_lhs_assignment = None
def analyse_expressions(self, env):
self.temp_nodes = []
self.max_ndim = max(op.type.ndim for op in self.operands)
- # self.lhs is an UnbroadcastDestNode
- self.lhs = self.lhs.lhs
- #self.lhs.analyse_types(env)
+ if isinstance(self.lhs, UnbroadcastDestNode):
+ self.lhs = self.lhs.lhs
+
self.lhs = self.lhs.coerce_to_simple(env)
self.rhs = SpecializationCaller(
@@ -685,29 +735,30 @@ def analyse_expressions(self, env):
for i, operand in enumerate(self.operands):
operand.analyse_types(env)
- self.check_overlap = CheckOverlappingMemoryNode(
- self.pos, dst=self.lhs.wrap_in_clone_node(),
- operands=self.operands)
- self.check_overlap.analyse_types(env)
+ if not self.acquire_slice:
+ self.check_overlap = CheckOverlappingMemoryNode(
+ self.pos, dst=self.lhs.wrap_in_clone_node(),
+ operands=self.operands)
+ self.check_overlap.analyse_types(env)
- self.temp_dst = TempSliceMemory(self.rhs.pos, target=self.rhs)
- self.temp_dst.analyse_types(env)
+ self.final_assignment_node = self.final_assignment()
+ self.final_assignment_node.analyse_types(env)
+ self.temp_nodes.append(self.final_assignment_node.target)
+
+ self.final_broadcast = BroadcastNode(
+ self.pos, operands=[self.rhs], max_ndim=self.lhs.type.ndim,
+ dst_slice=self.lhs, init_shape=False)
+ self.final_broadcast.analyse_types(env)
+
+ self.temp_dst = TempSliceMemory(self.rhs.pos, target=self.rhs)
+ self.temp_dst.analyse_types(env)
self.broadcast = BroadcastNode(self.pos,
operands=self.operands,
max_ndim=self.max_ndim,
dst_slice=self.rhs)
self.broadcast.analyse_types(env)
- self.final_assignment_node = self.final_assignment()
- self.final_assignment_node.analyse_types(env)
- self.temp_nodes.append(self.final_assignment_node.target)
-
- self.final_broadcast = BroadcastNode(
- self.pos, operands=[self.rhs], max_ndim=self.lhs.type.ndim,
- dst_slice=self.lhs, init_shape=False)
- self.final_broadcast.analyse_types(env)
-
def final_assignment(self):
b = self.minicontext.astbuilder
typemapper = self.minicontext.typemapper
@@ -800,70 +851,81 @@ def generate_execution_code(self, code):
for scalar_op in self.scalar_operands:
scalar_op.generate_evaluation_code(code)
- code.putln("/* Check overlapping memory */")
- self.check_overlap.generate_evaluation_code(code)
+ if self.check_overlap:
+ code.putln("/* Check overlapping memory */")
+ self.check_overlap.generate_evaluation_code(code)
code.putln("/* Broadcast all operands in RHS expression */")
+ # create and initialize broadcasting flag
self.broadcast.generate_evaluation_code(code)
+ self.broadcast.init_broadcast_flag(code)
self.broadcast.generate_broadcasting_code(code)
- self.verify_final_shape(code)
-
- self.rhs.align_with_lhs(code)
- # Set rhs.data and rhs.strides
- code.putln("/* Allocate scratch space if needed */")
- code.putln("if (%s) {" % self.overlap())
- self.temp_dst.generate_evaluation_code(code)
- code.putln("} else {")
- # shut up compiler warnings
- code.putln("%s = NULL;" % self.temp_dst.data.result())
- self.init_rhs_temp(code)
- code.putln("}")
+ if not self.acquire_slice:
+ self.verify_final_shape(code)
+ self.rhs.align_with_lhs(code)
+ # Set rhs.data and rhs.strides
+ code.putln("/* Allocate scratch space if needed */")
+ code.putln("if (%s) {" % self.overlap())
+ self.temp_dst.generate_evaluation_code(code)
+ code.put("} else {")
+ # shut up compiler warnings
+ code.putln("%s = NULL;" % self.temp_dst.data.result())
+ self.init_rhs_temp(code)
+ code.putln("}")
+ else:
+ self.acquire_slice.generate_execution_code(code)
+ self.init_rhs_temp(code)
code.putln("/* Evaluate expression */")
self.rhs.broadcasting = self.broadcast.result()
self.rhs.generate_evaluation_code(code)
- self.final_broadcast.generate_evaluation_code(code)
- self.final_broadcast.init_broadcast_flag(code)
- code.putln("if (!%s) {" % self.overlap())
- self.advance_lhs_data_ptr(code)
- code.putln("}")
-
- code.putln("/* Broadcast final RHS and LHS */")
- self.final_assignment_node.target.generate_evaluation_code(code)
- self.final_broadcast.generate_broadcasting_code(code)
-
- code.putln("/* Final broadcasting assignment */")
- if self.lhs.type.ndim == self.rhs.type.ndim:
- code.putln("if (%s || %s) {" % (self.final_broadcast.result(),
- self.overlap()))
- # self.remove_rhs_offset(code)
- self.final_assignment_node.broadcasting = self.final_broadcast.result()
- self.final_assignment_node.generate_evaluation_code(code)
- if self.lhs.type.ndim == self.rhs.type.ndim:
+ if not self.acquire_slice:
+ self.final_broadcast.generate_evaluation_code(code)
+ self.final_broadcast.init_broadcast_flag(code)
+ code.putln("if (!%s) {" % self.overlap())
+ self.advance_lhs_data_ptr(code)
code.putln("}")
- code.putln("/* Cleanup */")
- code.putln("if (%s) {" % self.overlap())
- self.temp_dst.generate_disposal_code(code)
- self.temp_dst.free_temps(code)
- code.putln("}")
+ code.putln("/* Broadcast final RHS and LHS */")
+ self.final_assignment_node.target.generate_evaluation_code(code)
+ self.final_broadcast.generate_broadcasting_code(code)
+
+ code.putln("/* Final broadcasting assignment */")
+ if self.lhs.type.ndim == self.rhs.type.ndim:
+ code.putln("if (%s || %s) {" % (self.final_broadcast.result(),
+ self.overlap()))
+ # self.remove_rhs_offset(code)
+ self.final_assignment_node.broadcasting = self.final_broadcast.result()
+ self.final_assignment_node.generate_evaluation_code(code)
+ if self.lhs.type.ndim == self.rhs.type.ndim:
+ code.putln("}")
+
+ code.putln("/* Cleanup */")
+ code.putln("if (%s) {" % self.overlap())
+ self.temp_dst.generate_disposal_code(code)
+ self.temp_dst.free_temps(code)
+ code.putln("}")
+ else:
+ self.final_lhs_assignment.generate_execution_code(code)
self.dispose(code)
def dispose(self, code):
for child_attr in self.child_attrs:
- if child_attr == 'temp_dst':
+ if child_attr in ('temp_dst', 'acquire_slice',
+ 'final_lhs_assignment'):
continue
value_list = getattr(self, child_attr)
if not isinstance(value_list, list):
value_list = [value_list]
for node in value_list:
- node.generate_disposal_code(code)
- node.free_temps(code)
+ if node:
+ node.generate_disposal_code(code)
+ node.free_temps(code)
def need_wrapper_node(node):
"""
@@ -1020,7 +1082,11 @@ def visit_ExprNode(self, node):
return self.register_operand(node)
def visit_SingleAssignmentNode(self, node):
- return self.astbuilder.assign(self.visit(node.lhs.dst),
+ if isinstance(node.lhs, ExprNodes.MemoryCopySlice):
+ lhs = node.lhs.dst
+ else:
+ lhs = node.lhs
+ return self.astbuilder.assign(self.visit(lhs),
self.visit(node.rhs))
@elemental_dispatcher
@@ -1050,7 +1116,7 @@ def visit_ModuleNode(self, node):
self.visitchildren(node)
return node
- def visit_elemental(self, node, lhs=None):
+ def visit_elemental(self, node, lhs=None, acquire_slice=None):
self.in_elemental += 1
self.visitchildren(node)
self.in_elemental -= 1
@@ -1083,13 +1149,15 @@ def visit_elemental(self, node, lhs=None):
function = b.function(name, body, astmapper.funcargs,
posinfo=posinfo)
- astmapper.operands.remove(lhs)
+ #astmapper.operands.remove(lhs)
+ astmapper.operands.pop(0)
node = ElementalNode(node.pos,
operands=astmapper.operands,
scalar_operands=astmapper.scalar_operands,
rhs_function=function,
minicontext=self.minicontext,
- lhs=lhs)
+ lhs=lhs,
+ acquire_slice=acquire_slice)
node.analyse_expressions(self.current_env())
#node = Nodes.ExprStatNode(node.pos, expr=node)
return node
@@ -1102,21 +1170,90 @@ def visit_ExprNode(self, node):
return node
+ def _acquire_new_memview(self, lhs, rhs, lazy_rhs, env):
+ """
+ Acquire a new memoryview slice, assigning to an lhs variable which
+ may be a memoryview or an object. The rhs is the broadcasted rhs
+ expression.
+ """
+ if not lhs.type.is_pyobject:
+ type = lhs.type
+ else:
+ type = rhs.type
+
+ # cyarray = <dtype[:broadcasted_shape[0]]> malloc(broadcasted_shape[0])
+ temp_cy_array = TempCythonArrayNode(
+ rhs.pos, dest_array_type=type, rhs=lazy_rhs)
+
+ # cdef dtype[:] tmp
+ tmp_lhs = TempSliceStruct(
+ lhs.pos, dtype=type.dtype, axes=type.axes, ndim=type.ndim)
+ tmp_lhs.analyse_types(env)
+ tmp_lhs = tmp_lhs.wrap_in_clone_node()
+
+ # tmp = cyarray
+ acquire_assignment = Nodes.SingleAssignmentNode(
+ lhs.pos, lhs=tmp_lhs.arg, rhs=temp_cy_array)
+ acquire_assignment.analyse_expressions(env)
+
+ # Final assignment, after elemental expression is executed:
+ # lhs = tmp
+ final_assignment = Nodes.SingleAssignmentNode(lhs.pos,
+ lhs=lhs, rhs=tmp_lhs)
+ final_assignment.analyse_expressions(env)
+ return lhs, rhs, tmp_lhs, acquire_assignment, final_assignment
+
+ def _create_new_array_node(self, lhs, rhs, env):
+ """
+ In an assignment like 'b = a + a', we need to create a new array 'b'.
+ We create a cython.view.array, and assign it to variable 'b'.
+
+ Todo: implement temporary memoryview slices without needing the GIL!
+ """
+ lazy_rhs = ExprNodes.ProxyNode(rhs)
+ lhs, rhs, tmp_lhs, acquire_assignment, final_assignment = \
+ self._acquire_new_memview(lhs, rhs, lazy_rhs, env)
+
+ # create elemental assignment
+ elemental_assignment = Nodes.SingleAssignmentNode(
+ lhs.pos, lhs=tmp_lhs.arg, rhs=rhs)
+
+ elemental_node = self.visit_elemental(elemental_assignment,
+ lhs=tmp_lhs.arg,
+ acquire_slice=acquire_assignment)
+
+ lazy_rhs.arg = elemental_node.rhs
+ lazy_rhs.proxy_type()
+ elemental_node.final_lhs_assignment = final_assignment
+ return elemental_node
+ #return [elemental_node, final_assignment]
+
def visit_SingleAssignmentNode(self, node):
+ if not (node.lhs.is_elemental or node.rhs.is_elemental):
+ self.visitchildren(node)
+ return node
+
+ env = self.current_env()
+
if (isinstance(node.lhs, ExprNodes.MemoryCopySlice) and
node.lhs.is_elemental):
-
assert not self.in_elemental
node.is_elemental = True
node.lhs.dst = UnbroadcastDestNode(
- node.pos, lhs=node.lhs.dst.coerce_to_temp(self.current_env()),
+ node.pos, lhs=node.lhs.dst.coerce_to_temp(env),
rhs=node.rhs)
- node.lhs.dst.analyse_types(self.current_env())
+ node.lhs.dst.analyse_types(env)
+
return self.visit_elemental(node, node.lhs.dst)
+ elif node.lhs.is_name and node.rhs.is_elemental:
+ if isinstance(node.rhs, ExprNodes.CoerceToPyTypeNode):
+ node.rhs = node.rhs.arg
+ return self._create_new_array_node(node.lhs, node.rhs, env)
+ else:
+ error(node.pos, "Invalid elementwise assignment")
+ return
- self.visitchildren(node)
- return node
def load_utilities(env):
from Cython.Compiler import CythonScope
View
4 Cython/Utility/Buffer.c
@@ -928,6 +928,10 @@ static struct __pyx_typeinfo_string __Pyx_TypeInfoToFormat(__Pyx_TypeInfo *type)
else
*buf = 'g';
break;
+ case 'O':
+ *buf = 'O';
+ break;
+ /* Note: pointers are disallowed at compile time */
}
return result;
View
20 Cython/Utility/MemoryView.pyx
@@ -251,7 +251,27 @@ cdef array array_cwrapper(tuple shape, Py_ssize_t itemsize, char *format,
return result
+@cname('__pyx_array_new_simple')
+cdef array array_cwrapper_simple(Py_ssize_t *shape, int ndim, Py_ssize_t itemsize,
+ object format, char mode):
+ cdef int i
+ cdef Py_ssize_t size = itemsize
+
+ for i in range(ndim):
+ size *= shape[i]
+ cdef char *buf = <char *> malloc(size)
+ if buf == NULL:
+ raise MemoryError
+
+ if mode == 'C':
+ dst_mode = "c"
+ else:
+ dst_mode = "fortran"
+ cdef array result = array_cwrapper(tuple([shape[i] for i in range(ndim)]),
+ itemsize, format, dst_mode, buf)
+ result.callback_free_data = free
+ return result
#
### Memoryview constants and cython.view.memoryview class
#
2 Cython/minivect
@@ -1 +1 @@
-Subproject commit 9e7f418d6278c3d0f289d399099c707b065af544
+Subproject commit d66bb051d62c8465c914425ae6608ad379d973bc
View
16 tests/array_expressions/elementwise.pyx
@@ -97,22 +97,6 @@ def test_arbitrary_dtypes(fused_dtype_t[:] m1, fused_dtype_t[::1] m2):
return np.asarray(m1)
@testcase
-def test_overlapping_memory(fused_dtype_t[:] m1, fused_dtype_t[:, :] m2):
- """
- >>> m1, m2 = operands('l')
- >>> m2 = m2[:2]
- >>> test_overlapping_memory(m1, m2)
- >>> m1
- array([0, 0, 1, 2, 3, 4, 5, 6, 7, 8])
- >>> m2
- array([[10, 12, 14, 16, 18, 20, 22, 24, 26, 28],
- [ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]])
- """
- # test reads after writes
- m2[...] = m2[::-1, :] + m1
- m1[1:] = m1[:-1]
-
-@testcase
def test_constant_scalar_complex_arguments(double complex[:] m):
"""
>>> test_constant_scalar_complex_arguments(np.arange(10, dtype=np.complex128))
View
44 tests/array_expressions/new_memory.pyx
@@ -0,0 +1,44 @@
+# tag: numpy
+# mode: run
+
+include "utils.pxi"
+
+@testcase
+def test_overlapping_memory(fused_dtype_t[:] m1, fused_dtype_t[:, :] m2):
+ """
+ >>> m1, m2 = operands('l')
+ >>> m2 = m2[:2]
+ >>> test_overlapping_memory(m1, m2)
+ >>> m1
+ array([0, 0, 1, 2, 3, 4, 5, 6, 7, 8])
+ >>> m2
+ array([[10, 12, 14, 16, 18, 20, 22, 24, 26, 28],
+ [ 0, 2, 4, 6, 8, 10, 12, 14, 16, 18]])
+ """
+ # test reads after writes
+ m2[...] = m2[::-1, :] + m1
+ m1[1:] = m1[:-1]
+
+@testcase
+def test_allocate_new_memory_simple(fused_dtype_t[:] m):
+ """
+ >>> test_allocate_new_memory_simple(np.arange(10, dtype=np.longdouble))
+ array([ 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], dtype=float128)
+ >>> test_allocate_new_memory_simple(object_range(10))
+ array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], dtype=object)
+ """
+ result = m + m
+ return np.asarray(result)
+
+@testcase
+def test_allocate_new_memory_typed(fused_dtype_t[:] m):
+ """
+ >>> test_allocate_new_memory_typed(np.arange(10, dtype=np.longdouble))
+ array([ 0.0, 2.0, 4.0, 6.0, 8.0, 10.0, 12.0, 14.0, 16.0, 18.0], dtype=float128)
+ >>> test_allocate_new_memory_typed(object_range(10))
+ array([0, 2, 4, 6, 8, 10, 12, 14, 16, 18], dtype=object)
+ """
+ cdef fused_dtype_t[:] result
+
+ result = m + m
+ return np.asarray(result)

0 comments on commit 62f8783

Please sign in to comment.