Opened 11 years ago
Closed 10 years ago
#6245 closed enhancement (fixed)
make a custom infix operator decorator
Reported by: | jason | Owned by: | cwitty |
---|---|---|---|
Priority: | major | Milestone: | sage-4.4 |
Component: | misc | Keywords: | |
Cc: | cwitty, hivert, mhansen, kcrisman | Merged in: | sage-4.4.alpha2 |
Authors: | Jason Grout, Carl Witty, Florent Hivert | Reviewers: | Ross Kyprianou |
Report Upstream: | N/A | Work issues: | |
Branch: | Commit: | ||
Dependencies: | Stopgaps: |
Description
It would be nice to incorporate the code developed in http://groups.google.com/group/sage-devel/browse_thread/thread/100de89e7d402134/fe89570b403344ae (and in the same spirit as the backslash operator). An example of the final developed code is at http://sagenb.org/home/pub/565.
Attachments (1)
Change History (18)
comment:1 Changed 11 years ago by
- Cc cwitty hivert added
comment:2 Changed 10 years ago by
The patch is an initial go at this. The documentation for the function needs to change (it still is just the same docs as the code I followed in plot/misc.py).
Here are some examples:
sage: from sage.misc.misc import infix_operator sage: # A post-fix operator sage: @infix_operator('or') sage: def pipe(a,b): ... return b(a) ... sage: print (x |pipe| cos) sage: print pi |pipe| n sage: print pi |pipe| n |pipe| cos cos(x) 3.14159265358979 -1.00000000000000 sage: # an infix dot product sage: @infix_operator('multiply') sage: def dot(a,b): ... return a.dot_product(b) ... ... sage: vector([1,2]) *dot* vector([2,4]) 10 sage: # an infix sum sage: @infix_operator('add') sage: def esum(a,b): ... return [i+j for i,j in zip(a,b)] ... sage: [1,2,4] +esum+ [3,-1,2] [4, 1, 6]
comment:3 Changed 10 years ago by
- Cc mhansen added
- Summary changed from make a custom infix operator decorator to [with patch, needs work] make a custom infix operator decorator
CCing mhansen, since he probably has very enlightening things to say about how I dealt with the decorators :).
comment:4 Changed 10 years ago by
comment:5 Changed 10 years ago by
I cleaned up the code a bit and made much better documentation.
comment:6 Changed 10 years ago by
- Cc kcrisman added
- Summary changed from [with patch, needs work] make a custom infix operator decorator to [with patch, needs review] make a custom infix operator decorator
comment:7 follow-up: ↓ 8 Changed 10 years ago by
I have no comment on the technical side, but wonder - are you limiting "object" to be or, add, or multiply? What if someone wanted to create an infix for, say, factorial - would they have to change this code?
comment:8 in reply to: ↑ 7 Changed 10 years ago by
Replying to kcrisman:
I have no comment on the technical side, but wonder - are you limiting "object" to be or, add, or multiply? What if someone wanted to create an infix for, say, factorial - would they have to change this code?
I only chose a few precedence levels (that's really the issue here). There's no reason we couldn't put all of the special functions in (like subtract, for example).
comment:9 Changed 10 years ago by
- Report Upstream set to N/A
Applied patch to 4.3.2
(Cool functionality - Can review quickly once this is addressed)
applying trac-6245-infix-decorator.patch patching file sage/misc/misc.py Hunk #1 FAILED at 2219 1 out of 1 hunks FAILED -- saving rejects to file sage/misc/misc.py.rej patch failed, unable to continue (try -v) patch failed, rejects left in working dir errors during apply, please fix and refresh trac-6245-infix-decorator.patch
comment:10 follow-up: ↓ 12 Changed 10 years ago by
I rebased the patch to Sage 4.3.4. Can you look at it again?
comment:11 Changed 10 years ago by
- Summary changed from [with patch, needs review] make a custom infix operator decorator to make a custom infix operator decorator
comment:12 in reply to: ↑ 10 Changed 10 years ago by
Replying to jason:
I rebased the patch to Sage 4.3.4. Can you look at it again?
Will aim to look at this later today
comment:13 Changed 10 years ago by
Can confirm all examples work as indicated and all tests passed for me
sage: def dot(a,b): sage: return a.dot_product(b) sage: dot=infix_operator('multiply')(dot) sage: u=vector([1,2,3]) sage: v=vector([5,4,3]) sage: u *dot* v 22 # Also these examples here show precedence works as # expected (i.e. * before +) # sage: def eadd(a,b): sage: return a.parent([i+j for i,j in zip(a,b)]) sage: sage: eadd=infix_operator('add')(eadd) sage: u=vector([1,2,3]) sage: v=vector([5,4,3]) sage: print u +eadd+ v sage: print 2*u +eadd+ v sage: print v +eadd+ 2*u sage: print v +eadd+ u*2 sage: print (v +eadd+ u)*2 (6, 6, 6) (7, 8, 9) (7, 8, 9) (7, 8, 9) (12, 12, 12) # Last example: function composition not commutative as expected sage: def thendo(a,b): return b(a) sage: thendo=infix_operator('or')(thendo) sage: print x |thendo| cos |thendo| (lambda x: x^2) sage: print x |thendo| (lambda x: x^2) |thendo| cos cos(x)^2 cos(x^2)
comment:14 follow-up: ↓ 15 Changed 10 years ago by
Noticed references to __rmul__
in the code so I thought I might try setting up a situation
where __mul__
fails so __rmul__
would be exercised. In setting up the test code, I got an
error but cant see whats wrong (any thoughts?)
class Fraction: def __init__(self, numerator, denominator=1): self.numerator = numerator self.denominator = denominator def __str__(self): return "%d/%d" % (self.numerator, self.denominator) def zmul(self, other): return Fraction(self.numerator*other.numerator, self.denominator*other.denominator) def zrmul(self, other): return Fraction(other*self.numerator, self.denominator) def __mul__(self, other): return Fraction(self.numerator*other.numerator, self.denominator*other.denominator) def __rmul__(self, other): return Fraction(other*self.numerator,self.denominator) # multiplication operator using methods print Fraction(2,3).zmul(Fraction(5,4))
10/12
# multiplication infix operator def dot(a,b): return a.zmul(b) dot = infix_operator('multiply')(dot) print Fraction(2,3) *dot* Fraction(5,4)
crashes with
Traceback (most recent call last): File "<stdin>", line 1, in <module> File "_sage_input_59.py", line 9, in <module> open("___code___.py","w").write("# -*- coding: utf-8 -*-\n" + _support_.preparse_worksheet_cell(base64.b64decode("Z...<abbreviated>...Y="),globals())+"\n"); execfile(os.path.abspath("___code___.py")) File "", line 1, in <module> File "/tmp/tmpIR_EGe/___code___.py", line 8, in <module> u *dot* v File "", line 1, in <module> File "", line 13, in __mul__ AttributeError: dot instance has no attribute 'numerator'
comment:15 in reply to: ↑ 14 Changed 10 years ago by
Replying to rossk:
Noticed references to
__rmul__
in the code so I thought I might try setting up a situation
where
__mul__
fails so__rmul__
would be exercised. In setting up the test code, I got an
error but cant see whats wrong (any thoughts?)
The problem is that your mul function does not check its arguments. Note that:
sage: Fraction(2,3)*matrix(2,1,[1,2]) Traceback (click to the left of this block for traceback) ... AttributeError: 'sage.matrix.matrix_integer_dense.Matrix_integer_dense' object has no attribute 'numerator'
Instead, you should check your arguments in the mul function before blindly calling the .numerator method, or at least you should catch the error, like this:
class Fraction: def __init__(self, numerator, denominator=1): self.numerator = numerator self.denominator = denominator def __str__(self): return "Fraction(%d,%d)" % (self.numerator, self.denominator) __repr__=__str__ def zmul(self, other): return Fraction(self.numerator*other.numerator, self.denominator*other.denominator) def __mul__(self, other): try: return Fraction(self.numerator*other.numerator, self.denominator*other.denominator) except: return NotImplemented
comment:16 Changed 10 years ago by
- Reviewers set to Ross Kyprianou
- Status changed from needs_review to positive_review
Thanks Jason - I appreciate what your code is doing a bit more now and see where I erred - thanks for the explanation. I would think these are enough tests to update this to a positive review.
class Fraction: def __init__(self, numerator, denominator=1): self.numerator = numerator self.denominator = denominator def __str__(self): return "Fraction(%d,%d)" % (self.numerator, self.denominator) __repr__=__str__ def zmul(self, other): return Fraction(self.numerator*other.numerator, self.denominator*other.denominator) def __mul__(self, other): try: return Fraction(self.numerator*other.numerator, self.denominator*other.denominator) except: return NotImplemented def __rmul__(self, other): try: return Fraction(other*self.numerator,self.denominator) except: return NotImplemented def dot(a,b): return a*b dot = infix_operator('multiply')(dot) u = Fraction(2,3) v = Fraction(5,4) print u *dot* v print 3 *dot* v
comment:17 Changed 10 years ago by
- Merged in set to sage-4.4.alpha2
- Resolution set to fixed
- Status changed from positive_review to closed
Merged into 4.4.alpha2.
For reference, the code is:
And several examples (doctests?) are: