[pypy-svn] pypy numpy-exp: Make auto-lazy-forcing not break semantics.

alex_gaynor commits-noreply at bitbucket.org
Thu May 5 01:04:02 CEST 2011


Author: Alex Gaynor <alex.gaynor at gmail.com>
Branch: numpy-exp
Changeset: r43899:1486788fda44
Date: 2011-05-04 19:03 -0400
http://bitbucket.org/pypy/pypy/changeset/1486788fda44/

Log:	Make auto-lazy-forcing not break semantics.

diff --git a/pypy/module/micronumpy/numarray.py b/pypy/module/micronumpy/numarray.py
--- a/pypy/module/micronumpy/numarray.py
+++ b/pypy/module/micronumpy/numarray.py
@@ -145,6 +145,9 @@
 JITCODES = {}
 
 class BaseArray(Wrappable):
+    def __init__(self):
+        self.invalidates = []
+
     def force(self):
         code = self.compile()
         try:
@@ -156,16 +159,24 @@
         # (we still have to compile new bytecode, but too bad)
         return compute(code)
 
+    def invalidated(self):
+        for arr in self.invalidates:
+            arr.force_if_needed()
+        self.invalidates = []
+
     def _binop_impl(bytecode):
         def impl(self, space, w_other):
             if isinstance(w_other, BaseArray):
-                return space.wrap(BinOp(bytecode, self, w_other))
+                res = space.wrap(BinOp(bytecode, self, w_other))
+                w_other.invalidates.append(res)
             else:
-                return space.wrap(BinOp(
+                res = space.wrap(BinOp(
                     bytecode,
                     self,
                     FloatWrapper(space.float_w(w_other))
                 ))
+            self.invalidates.append(res)
+            return res
         return func_with_new_name(impl, "binop_%s_impl" % bytecode)
 
     descr_add = _binop_impl("a")
@@ -182,6 +193,7 @@
     """
 
     def __init__(self, float_value):
+        BaseArray.__init__(self)
         self.float_value = float_value
 
     def compile(self):
@@ -193,18 +205,39 @@
     """
 
     def __init__(self, opcode, left, right):
+        BaseArray.__init__(self)
         self.opcode = opcode
         self.left = left
         self.right = right
 
+        self.forced_result = None
+
     def compile(self):
         left_code = self.left.compile()
         right_code = self.right.compile()
         return left_code.merge(self.opcode, right_code)
 
-BaseArray.typedef = TypeDef(
+    def force_if_needed(self):
+        if self.forced_result is None:
+            self.forced_result = self.force()
+
+    @unwrap_spec(item=int)
+    def descr_getitem(self, space, item):
+        self.force_if_needed()
+        return self.forced_result.descr_getitem(space, item)
+
+    @unwrap_spec(item=int, value=float)
+    def descr_setitem(self, space, item, value):
+        self.forced_if_needed()
+        self.invalidated()
+        return self.forced_result.descr_setitem(space, item, value)
+
+
+BinOp.typedef = TypeDef(
     'Operation',
-    force = interp2app(BaseArray.force),
+    __getitem__ = interp2app(BinOp.descr_getitem),
+    __setitem__ = interp2app(BinOp.descr_setitem),
+
     __add__ = interp2app(BaseArray.descr_add),
     __sub__ = interp2app(BaseArray.descr_sub),
     __mul__ = interp2app(BaseArray.descr_mul),
@@ -213,6 +246,7 @@
 
 class SingleDimArray(BaseArray):
     def __init__(self, size):
+        BaseArray.__init__(self)
         self.size = size
         self.storage = lltype.malloc(TP, size, zero=True,
                                      flavor='raw', track_allocation=False)
@@ -239,11 +273,9 @@
         if item > self.size:
             raise operationerrfmt(space.w_TypeError,
               '%d above array size', item)
+        self.invalidated()
         self.storage[item] = value
 
-    def force(self):
-        return self
-
     def __del__(self):
         lltype.free(self.storage, flavor='raw')
 
@@ -270,5 +302,4 @@
     __sub__ = interp2app(BaseArray.descr_sub),
     __mul__ = interp2app(BaseArray.descr_mul),
     __div__ = interp2app(BaseArray.descr_div),
-    force = interp2app(SingleDimArray.force),
 )
\ No newline at end of file
diff --git a/pypy/module/micronumpy/test/test_numpy.py b/pypy/module/micronumpy/test/test_numpy.py
--- a/pypy/module/micronumpy/test/test_numpy.py
+++ b/pypy/module/micronumpy/test/test_numpy.py
@@ -23,7 +23,6 @@
         from numpy import array
         a = array(range(5))
         b = a + a
-        b = b.force()
         for i in range(5):
             assert b[i] == i + i
 
@@ -32,7 +31,6 @@
         a = array(range(5))
         b = array(reversed(range(5)))
         c = a + b
-        c = c.force()
         for i in range(5):
             assert c[i] == 4
 
@@ -40,14 +38,13 @@
         from numpy import array
         a = array(range(5))
         b = a + 5
-        b = b.force()
         for i in range(5):
             assert b[i] == i + 5
 
     def test_subtract(self):
         from numpy import array
         a = array(range(5))
-        b = (a - a).force()
+        b = a - a
         for i in range(5):
             assert b[i] == 0
 
@@ -55,14 +52,14 @@
         from numpy import array
         a = array(range(5))
         b = array([1, 1, 1, 1, 1])
-        c = (a - b).force()
+        c = a - b
         for i in range(5):
             assert c[i] == i - 1
 
     def test_subtract_constant(self):
         from numpy import array
         a = array(range(5))
-        b = (a - 5).force()
+        b = a - 5
         for i in range(5):
             assert b[i] == i - 5
 
@@ -70,7 +67,6 @@
         from numpy import array
         a = array(range(5))
         b = a * a
-        b = b.force()
         for i in range(5):
             assert b[i] == i * i
 
@@ -78,14 +74,13 @@
         from numpy import array
         a = array(range(5))
         b = a * 5
-        b = b.force()
         for i in range(5):
             assert b[i] == i * 5
 
     def test_div(self):
         from numpy import array
         a = array(range(1, 6))
-        b = (a / a).force()
+        b = a / a
         for i in range(5):
             assert b[i] == 1
 
@@ -93,17 +88,25 @@
         from numpy import array
         a = array(range(5))
         b = array([2, 2, 2, 2, 2])
-        c = (a / b).force()
+        c = a / b
         for i in range(5):
             assert c[i] == i / 2.0
 
     def test_div_constant(self):
         from numpy import array
         a = array(range(5))
-        b = (a / 5.0).force()
+        b = a / 5.0
         for i in range(5):
             assert b[i] == i / 5.0
 
+    def test_auto_force(self):
+        from numpy import array
+        a = array(range(5))
+        b = a - 1
+        a[2] = 3
+        for i in range(5):
+            assert b[i] == i - 1
+
 class AppTestNumpy(object):
     def setup_class(cls):
         py.test.skip("unimplemented")


More information about the Pypy-commit mailing list