[sage-devel] Re: making new infix operators
On 9/27/07, Fernando Perez wrote: On 9/27/07, Robert Bradshaw wrote: I agree with your caution, but don't see how the examples above mess anything up. If an object B wants to pretend to be immutable, it merely has to check that nothing points to it before mutating itself (and, in mutating itself, don't mutate objects that have other pointers to them). Yup, I was just trying to illustrate the issues that could arise if one uses the assumption that refcount==1 == no side effects on in-place operations. But with the proper care, it's a perfectly valid approach for optimizing expressions involving immutable objects. Given Bill's surprise, it probably wasn't totally useless to provide these examples :) Very useful indeed, thanks! :-) Regards, Bill Page. --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Mike Hansen wrote: The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? I think A.union(B) should leave A and B unchanged and return a new graph object since that's pretty much how everything else works in Python. In-place operations feel very out-of-place ;-) That's what I always thought to. But then I saw the Python sort() function, or the reverse() function, etc. and grudgingly accepted that the python way was to modify things in place. In the end, I want two options: to modify things in-place or to return a new object. I also want some sort of consistency so that the user isn't constantly guessing what a function will do. After playing with things for a bit, I decided that the easiest consistent thing to do was to modify in-place and call A.copy().whatever() when I wanted to return a new object. That way a copy was explicitly denoted with somewhat minimal syntax, the general case was consistent, and we still had access to both options. Would you suggest doing things the other way? Return a new copy by default and always have: A=A.union(B) to modify A? It's slightly worse performance-wise, it seems, because we are always making copies and throwing old copies away. Especially if we have the above (A.union(B).union(C).union(D).union(E))---then we make 4 new copies of things and throw away all but 1 of them. -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Craig Citro wrote: The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) I actually think that this looks very clear, despite the lack of infix operators. I agree -- especially since it looks like a literal translation of the same statement as written mathematics. However, I could see a strong argument that it could get too long -- it wouldn't take much work to change union to support something like this: A.union( [ B, C, D, E ] ) I originally was going to do things this way, but then decided the way I presented. However, you're right---it's a trivial change to allow both possibilities. I'll definitely add that. Thanks, -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Chris Chiasson wrote: It might be worth pointing out that adding new syntax in Mathematica is (usually) done by assignments to the function that transforms general two dimensional input into source code. That isn't really the same thing as adding a new operator to the language itself. Adding a new operator to the language itself means that you change the parser to interpret the new operator. That's exactly what you're doing in Mathematica by modifying the function that converts input to a nested list of function calls (defined by the user or by the base system library). So I guess I don't see a difference in your distinction between these two cases. Of course, I could be missing something here. In the above discussion, though, I was mainly referring to the way that Mathematica can take the same function and call it in prefix, infix, and postfix syntax. So you can write one function, disjoint_union, and call it via: disjoint_union[a,b], a `disjoint_union` b, or [a,b] // disjoint_union[#[[1]],#[[2]]] Thanks, -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 9/27/07, Jason Grout wrote: ... where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? I would rather union returns a new object rather than modifying A, it's much easier to reason about code that way, and fits better with the rest of SAGE. One could provide a different method to do union in place if one needs. I guess this brings up a much more general issue of conventions in SAGE (in general, should methods modify an object in-place or pass back a new object?). I'll post up a new thread to try to clarify and get feedback on what conventions there are/should be. There is another very relevant thread started by Robert Bradshaw on this list concerning the safety of in-place modifications: http://groups.google.com/group/sage-devel/browse_thread/thread/806cd958eb28ac3b/46655d7572d11ee6?#46655d7572d11ee6 http://www.sagemath.org:9002/sage_trac/ticket/624 In general one wants to do as much in-place modification as possible without risking unintended side-effects in other parts of the code. Since Python maintains a reference count for every object, this means that it is possible for an operation to know whether doing an in-place modification represents a risk. If there is no external reference to the object then there is no risk, so the operation should be done in-place. If the reference count is greater than zero, then the operation should (automatically) create a copy and then perform the operation on the copy. Using this convention, for the sake of efficiency all operations in Sage would be written to operate in-place but they would transparently create copies of the object as necessary in order to make side-effects impossible. Thus you can have your cake and eat it too. Thus in newgraph=A.union(B).union(C).union(D).union(E) would be done entirely in-place so that 'newgraph' becomes a modified version of A, while in newgraph1=A.union(B).union(C).union(D).union(E) newgraph2=A.union(B).union(C).union(D).union(E) a new copy of A is created and modified before it is assigned to 'newgraph2'. However we do not have to be aware of this as such. The only thing we need to remember is that there can be no side-effects. Regards, Bill Page. --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 9/27/07, Bill Page [EMAIL PROTECTED] wrote: There is another very relevant thread started by Robert Bradshaw on this list concerning the safety of in-place modifications: http://groups.google.com/group/sage-devel/browse_thread/thread/806cd958eb28ac3b/46655d7572d11ee6?#46655d7572d11ee6 http://www.sagemath.org:9002/sage_trac/ticket/624 It's worth mentioning that extensions that provide low-level views of memory, even if they are fully refcounted, break the assumptions about the refcount being a safe way of detecting how many accesses to the current object exist. Here's a simple example illustrating the point: import numpy as N import sys a = N.arange(10) sys.getrefcount(a) 2 b = a[::2] sys.getrefcount(a) 3 c = b[::2] sys.getrefcount(a) 3 c[:] = 999 a array([999, 1, 2, 3, 999, 5, 6, 7, 999, 9]) The last line modified a, even though the creation of the 'c' view over a's memory area did NOT increment a's refcount (since it did it via b, hence only b's refcount went up). This example is a bit contrived, but I just want to illustrate that extensions such as numpy (which is the basis of much of the numerical python code in existence) can make refcount-based arguments tricky. Cheers, f --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On Sep 27, 2007, at 4:43 AM, Jason Grout wrote: Mike Hansen wrote: The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? I think A.union(B) should leave A and B unchanged and return a new graph object since that's pretty much how everything else works in Python. In-place operations feel very out-of-place ;-) That's what I always thought to. But then I saw the Python sort() function, or the reverse() function, etc. and grudgingly accepted that the python way was to modify things in place. Yes, but note that sort and reverse don't return anything, which I think is a good convention. You don't have to worry about having two objects, and wondering if they're actually the same thing (as you do with an inplace operator that returns self). In the end, I want two options: to modify things in-place or to return a new object. I also want some sort of consistency so that the user isn't constantly guessing what a function will do. After playing with things for a bit, I decided that the easiest consistent thing to do was to modify in-place and call A.copy().whatever() when I wanted to return a new object. That way a copy was explicitly denoted with somewhat minimal syntax, the general case was consistent, and we still had access to both options. Would you suggest doing things the other way? Return a new copy by default and always have: A=A.union(B) to modify A? Yes. Or even A.inplace_union(B) (or some better name than that) which returns nothing. Otherwise, for example, every function that received a graph as an argument would have to make a copy to avoid side effects (or the calling function would always have to make a copy before passing in just in case). Explicitly making a copy everywhere seems cumbersome. It's slightly worse performance-wise, it seems, because we are always making copies and throwing old copies away. Especially if we have the above (A.union(B).union(C).union(D).union(E))---then we make 4 new copies of things and throw away all but 1 of them. True, this is the price to pay. It might be worth having a function that does things inplace if one needs the speed, but I like to avoid side effects in general. Though I think the syntax above is the clearest, A.union([B,C,D]) could be done more efficiently. - Robert --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On Sep 27, 2007, at 7:36 AM, Fernando Perez wrote: On 9/27/07, Bill Page [EMAIL PROTECTED] wrote: There is another very relevant thread started by Robert Bradshaw on this list concerning the safety of in-place modifications: http://groups.google.com/group/sage-devel/browse_thread/thread/ 806cd958eb28ac3b/46655d7572d11ee6?#46655d7572d11ee6 http://www.sagemath.org:9002/sage_trac/ticket/624 It's worth mentioning that extensions that provide low-level views of memory, even if they are fully refcounted, break the assumptions about the refcount being a safe way of detecting how many accesses to the current object exist. Here's a simple example illustrating the point: import numpy as N import sys a = N.arange(10) sys.getrefcount(a) 2 b = a[::2] sys.getrefcount(a) 3 c = b[::2] sys.getrefcount(a) 3 c[:] = 999 a array([999, 1, 2, 3, 999, 5, 6, 7, 999, 9]) The last line modified a, even though the creation of the 'c' view over a's memory area did NOT increment a's refcount (since it did it via b, hence only b's refcount went up). This example is a bit contrived, but I just want to illustrate that extensions such as numpy (which is the basis of much of the numerical python code in existence) can make refcount-based arguments tricky. You have a good point, though fortunately we're looking for refcounts so low that (essentially) nothing else can be holding onto it but the current stack frame. If b is pointing to a, it doesn't matter how many other things are (directly or indirectly). If nothing is pointing to it directly, it's difficult (but not impossible) for things to safely point to it indirectly. Also, we would only play this trick with SAGE elements, which we have more control over. Also, in this case, since things are referenced, if one changes so should the other. I gave this a lot of though, but I hope others will too since I can't be sure I didn't miss something. - Robert --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 9/27/07, Robert Bradshaw [EMAIL PROTECTED] wrote: You have a good point, though fortunately we're looking for refcounts so low that (essentially) nothing else can be holding onto it but the current stack frame. If b is pointing to a, it doesn't matter how many other things are (directly or indirectly). If nothing is pointing to it directly, it's difficult (but not impossible) for things to safely point to it indirectly. Also, we would only play this trick with SAGE elements, which we have more control over. The subtlety appears if the object you're dealing with is itself a view of something else: In [3]: a = N.arange(10) In [4]: b = a[::2] In [5]: sys.getrefcount(b) Out[5]: 2 In [6]: sys.getrefcount(a) Out[6]: 3 In [7]: c = a[::2] In [8]: sys.getrefcount(b) Out[8]: 2 In [9]: c.fill(999) In [10]: sys.getrefcount(b) Out[10]: 2 In [11]: b Out[11]: array([999, 999, 999, 999, 999]) In this case, b gets modified indirectly, even though its refcount never went above 2, so it would appear 'safe' by a refcounting argument. The problem is that b is itself a view onto another object (a) for which someone else (c) is holding also 'write access'. So while b appears to be 'isolated', it is still in reality subject to modification. Cheers, f --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Robert Bradshaw wrote: On Sep 27, 2007, at 4:43 AM, Jason Grout wrote: A=A.union(B) to modify A? Yes. Or even A.inplace_union(B) (or some better name than that) which returns nothing. Otherwise, for example, every function that received a graph as an argument would have to make a copy to avoid side effects (or the calling function would always have to make a copy before passing in just in case). Explicitly making a copy everywhere seems cumbersome. Thanks for the direct and good advice. I guess I had convinced myself that an object method would naturally do something to the object, so it was pretty natural to always modify in-place. It's slightly worse performance-wise, it seems, because we are always making copies and throwing old copies away. Especially if we have the above (A.union(B).union(C).union(D).union(E))---then we make 4 new copies of things and throw away all but 1 of them. True, this is the price to pay. It might be worth having a function that does things inplace if one needs the speed, but I like to avoid side effects in general. Though I think the syntax above is the clearest, A.union([B,C,D]) could be done more efficiently. Is this the sort of situation your ref-counting stuff would enhance? For example, in the above situation, if a new object were returned by union, then A.union(B) would not be referenced anywhere else in the system, so A.union(B).union(C) would actually not create a new object, but do the union in-place in A.union(B)? So the system would understand that the following code: def union(self, othergraph): g=self.copy() # Modify g to be the union of g and othergraph ... return g should make a copy of self in the A.union(B) case, but not make a copy when invoked in the .union(C) case above. Am I understanding the idea behind your ref-counting thread mentioned elsewhere? Thanks, Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On Sep 27, 2007, at 1:45 PM, Jason Grout wrote: Robert Bradshaw wrote: On Sep 27, 2007, at 4:43 AM, Jason Grout wrote: A=A.union(B) to modify A? Yes. Or even A.inplace_union(B) (or some better name than that) which returns nothing. Otherwise, for example, every function that received a graph as an argument would have to make a copy to avoid side effects (or the calling function would always have to make a copy before passing in just in case). Explicitly making a copy everywhere seems cumbersome. Thanks for the direct and good advice. I guess I had convinced myself that an object method would naturally do something to the object, so it was pretty natural to always modify in-place. What is natural depends totally on the context, but, as William mentioned, SAGE leans towards considering mathematical objects to be immutable. Personally, when I see A.union(B) I think (from a mathematical point of view) I'm getting a new object that's the union of A and B, rather than throwing all elements of B into A. On the other hand, something like A.add_vertex('x') makes me think I'm mutating A (and don't expect to get a new object back). Of course, these are fuzzy matters of personal opinion. It's slightly worse performance-wise, it seems, because we are always making copies and throwing old copies away. Especially if we have the above (A.union(B).union(C).union(D).union(E))---then we make 4 new copies of things and throw away all but 1 of them. True, this is the price to pay. It might be worth having a function that does things inplace if one needs the speed, but I like to avoid side effects in general. Though I think the syntax above is the clearest, A.union([B,C,D]) could be done more efficiently. Is this the sort of situation your ref-counting stuff would enhance? For example, in the above situation, if a new object were returned by union, then A.union(B) would not be referenced anywhere else in the system, so A.union(B).union(C) would actually not create a new object, but do the union in-place in A.union(B)? So the system would understand that the following code: def union(self, othergraph): g=self.copy() # Modify g to be the union of g and othergraph ... return g should make a copy of self in the A.union(B) case, but not make a copy when invoked in the .union(C) case above. Am I understanding the idea behind your ref-counting thread mentioned elsewhere? Yes, exactly. Currently its only implemented (though not yet a part of SAGE) for the arithmetic operators +,-,*,/ on SAGE elements. - Robert --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 9/27/07, Fernando Perez wrote: On 9/27/07, Robert Bradshaw wrote: You have a good point, though fortunately we're looking for refcounts so low that (essentially) nothing else can be holding onto it but the current stack frame. If b is pointing to a, it doesn't matter how many other things are (directly or indirectly). If nothing is pointing to it directly, it's difficult (but not impossible) for things to safely point to it indirectly. Also, we would only play this trick with SAGE elements, which we have more control over. The subtlety appears if the object you're dealing with is itself a view of something else: In [3]: a = N.arange(10) In [4]: b = a[::2] In [5]: sys.getrefcount(b) Out[5]: 2 In [6]: sys.getrefcount(a) Out[6]: 3 I think there might be some confusion here over what is an object and what is a reference to an object. The first object here is the result of 'N.arange(10)'. sage: sys.getrefcount( N.arange(10) ) 1 because using 'N.arange(10)' in this function call constitutes just 1 reference to this object. But when 'a' is also created as a reference to that object sage: a = N.arange(10) sage: sys.getrefcount(a) 2 the object denoted by 'N.arange(10)' already has two many references to safely be manipulated in-place! Remember that what gets passed to 'sys.getrefcount(a)' is the value of 'a', not 'a' itself. If we were to change this object, then then the object referenced by 'a' would also change and that is exactly the kind of side-effect that we want to avoid. So in this case if we can only do in-place operations on objects whose reference count is 1, you might wonder just how useful this could be ... But consider this: sage: sys.getrefcount([1,2]) 1 The expression '[1,2]' creates a list object. It's only reference is in the function call. So now write: sage: [1,2].__add__([3,5]).__add__([7,8]) [1, 2, 3, 5, 7, 8] where __add__ is a list method. According to the proposed semantics the __add__ operation above can safely operate in-place, i.e. first '3,5' is inserted (distructively) into the object '[1,2]' and then '7,8' is inserted into the *same* object. But if I write: sage: a=[1,2] sage: sys.getrefcount(a) 2 sage: a.__add__([3,5]).__add__([7,8]) [1, 2, 3, 5, 7, 8] then it is no longer safe to simply insert '3,5' into the object denoted by '[1,2]' since that will affect the value of 'a'. So instead we first of all make a new copy of the list '[1,2]' and perform the insertion into it. Regards, Bill Page. --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 9/27/07, Bill Page [EMAIL PROTECTED] wrote: On 9/27/07, Fernando Perez wrote: On 9/27/07, Robert Bradshaw wrote: You have a good point, though fortunately we're looking for refcounts so low that (essentially) nothing else can be holding onto it but the current stack frame. If b is pointing to a, it doesn't matter how many other things are (directly or indirectly). If nothing is pointing to it directly, it's difficult (but not impossible) for things to safely point to it indirectly. Also, we would only play this trick with SAGE elements, which we have more control over. The subtlety appears if the object you're dealing with is itself a view of something else: In [3]: a = N.arange(10) In [4]: b = a[::2] In [5]: sys.getrefcount(b) Out[5]: 2 In [6]: sys.getrefcount(a) Out[6]: 3 I think there might be some confusion here over what is an object and what is a reference to an object. The first object here is the result of 'N.arange(10)'. The problematic issue is that some extensions (numpy first and foremost, but this buffer API is going into the core of the language for 2.6/3.0) allow multiple, distinct *python objects* to share the same (or parts of the same) chunk of raw memory. The refcount mechanism only tracks python objects, not raw memory. Hopefully this helps: In [12]: a = N.arange(10) In [13]: sys.getrefcount(a) Out[13]: 2 Here the refcount is 2, as expected: the original array and the name 'a' pointing to it. Now, a naked, unassigned slice of the original array has a refcount of 1: In [14]: sys.getrefcount(a[::2]) Out[14]: 1 And yet, modifying it in-place has an effect on the original object (which we have called 'a'): In [15]: a[::2].fill(999) In [16]: a Out[16]: array([999, 1, 999, 3, 999, 5, 999, 7, 999, 9]) The slice 'a[::2]' has a refcount of 1, so it appears 'safe' from a purely refcounting argument. But it actually shares memory with the original N.arange(10) object, so it can overwrite it. Note that I'm only pointing this out as a word of caution that there are cases where refcounting arguments can be a bit delicate. The basic idea of reusing in-place objects as an optimization is a very good one for cases where you know that your underlying objects simply don't expose any interface for antics like the above. Cheers, f --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Robert Miller wrote: The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? This is false. The union function returns a new graph, so the first method already works. Yes, it currently does this; good point, I should have mentioned that that was how it was currently programmed (did I not? Sorry). I was proposing a different design. My whole inquiry has to do with what should (for a suitably defined should :) the interface be and are there official guidelines (or should there be official guidelines) for this type of operation on an object so that there is consistency across SAGE. I think the overall answer that I'm getting is that things (mathematical objects) should generally be considered immutable unless it really doesn't make sense or is very inconvenient to consider them immutable. We can later use ref-counting tricks to cut down on the resulting overhead, so don't worry too much about it. I think that that sounds like a great convention. Thanks, -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 9/27/07, Fernando Perez wrote: ... In [12]: a = N.arange(10) In [13]: sys.getrefcount(a) Out[13]: 2 Here the refcount is 2, as expected: the original array and the name 'a' pointing to it. Now, a naked, unassigned slice of the original array has a refcount of 1: In [14]: sys.getrefcount(a[::2]) Out[14]: 1 Whoa! This looks wrong to me. The slice of some larger object (which in turn has a reference to it) should not have a reference count of 1. (-: Ok, maybe it should be 1.1 but not 1! ;-) The mutable larger object in which it is contained certainly *references* parts of itself. No? And yet, modifying it in-place has an effect on the original object (which we have called 'a'): In [15]: a[::2].fill(999) In [16]: a Out[16]: array([999, 1, 999, 3, 999, 5, 999, 7, 999, 9]) Uggh! Awful. :-( The slice 'a[::2]' has a refcount of 1, so it appears 'safe' from a purely refcounting argument. But it actually shares memory with the original N.arange(10) object, so it can overwrite it. As far as I am concerned sharing memory is equivalent to a reference. Note that I'm only pointing this out as a word of caution that there are cases where refcounting arguments can be a bit delicate. The basic idea of reusing in-place objects as an optimization is a very good one for cases where you know that your underlying objects simply don't expose any interface for antics like the above. I think your points are well taken. There certainly seems to be some confusion and a lot written about this subject on the web. For example from the Python docs: http://docs.python.org/ext/refcountsInPython.html - Nobody ``owns'' an object; however, you can own a reference to an object. An object's reference count is now defined as the number of owned references to it. The owner of a reference is responsible for calling Py_DECREF() when the reference is no longer needed. Ownership of a reference can be transferred. --- And it defines the concept of a borrowed reference, etc., etc. Regards, Bill Page. --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On Sep 27, 2007, at 2:47 PM, Fernando Perez wrote: The problematic issue is that some extensions (numpy first and foremost, but this buffer API is going into the core of the language for 2.6/3.0) allow multiple, distinct *python objects* to share the same (or parts of the same) chunk of raw memory. The refcount mechanism only tracks python objects, not raw memory. Yes, but that's all we care about. If an implementation of an object points to the memory of another object, it assume that the other chunk of memory may change. Hopefully this helps: In [12]: a = N.arange(10) In [13]: sys.getrefcount(a) Out[13]: 2 Here the refcount is 2, as expected: the original array and the name 'a' pointing to it. Now, a naked, unassigned slice of the original array has a refcount of 1: In [14]: sys.getrefcount(a[::2]) Out[14]: 1 And yet, modifying it in-place has an effect on the original object (which we have called 'a'): In [15]: a[::2].fill(999) In [16]: a Out[16]: array([999, 1, 999, 3, 999, 5, 999, 7, 999, 9]) The slice 'a[::2]' has a refcount of 1, so it appears 'safe' from a purely refcounting argument. But it actually shares memory with the original N.arange(10) object, so it can overwrite it. This is by design, and a slice object wants to mutate the underlying object. And slice objects know that their mutations may have side effects. The object being sliced ('a' in this example) knows it is unsafe because b holds a reference to it. (If b held a pointer into the memory of a, but no reference, that memory would be in risk of getting recycled) . Note that I'm only pointing this out as a word of caution that there are cases where refcounting arguments can be a bit delicate. The basic idea of reusing in-place objects as an optimization is a very good one for cases where you know that your underlying objects simply don't expose any interface for antics like the above. I agree with your caution, but don't see how the examples above mess anything up. If an object B wants to pretend to be immutable, it merely has to check that nothing points to it before mutating itself (and, in mutating itself, don't mutate objects that have other pointers to them). - Robert --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Well, I don't want to take the thread off topic, but in the standard (documented) way of creating new operators, it is done with an assignment to MakeExpression. This will work in a notebook, where boxes are transformed into expressions before execution, but it will not work in a .m file, because that isn't processed by MakeExpression. On Sep 27, 6:57 am, Jason Grout [EMAIL PROTECTED] wrote: Chris Chiasson wrote: It might be worth pointing out that adding new syntax in Mathematica is (usually) done by assignments to the function that transforms general two dimensional input into source code. That isn't really the same thing as adding a new operator to the language itself. Adding a new operator to the language itself means that you change the parser to interpret the new operator. That's exactly what you're doing in Mathematica by modifying the function that converts input to a nested list of function calls (defined by the user or by the base system library). So I guess I don't see a difference in your distinction between these two cases. Of course, I could be missing something here. In the above discussion, though, I was mainly referring to the way that Mathematica can take the same function and call it in prefix, infix, and postfix syntax. So you can write one function, disjoint_union, and call it via: disjoint_union[a,b], a `disjoint_union` b, or [a,b] // disjoint_union[#[[1]],#[[2]]] Thanks, -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Mike Hansen wrote: Since I don't think that graphs and polytopes fall under the SAGE coercion model, overloading operators is pretty straightforward. You just need to define the __add__ method in your class. x + y will call x.__add__(y). sage: class Foo: : def __add__(self, y): : return 42 : sage: a = Foo() sage: b = Foo() sage: a + b 42 sage: b + a 42 Note that you'll want to do some type-checking so that y is what you actually think it should be. Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
No, I don't think you can make custom infix operators in Python. With something as long as 'boxproduct', you'd probably just be better off making it a method. --Mike On 9/26/07, Jason Grout [EMAIL PROTECTED] wrote: Mike Hansen wrote: Since I don't think that graphs and polytopes fall under the SAGE coercion model, overloading operators is pretty straightforward. You just need to define the __add__ method in your class. x + y will call x.__add__(y). sage: class Foo: : def __add__(self, y): : return 42 : sage: a = Foo() sage: b = Foo() sage: a + b 42 sage: b + a 42 Note that you'll want to do some type-checking so that y is what you actually think it should be. Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Nostalgia for Algol68 ! The line OP OVER = (INT n,d)RAT: cancel(RAT(n,d)); is taken from my implementation of rational numbers so one half could be written 1 OVER 2 and I could also write DET M for the determinant of a matrix (ok, that's prefix not infix). Don't mock, my original tables of elliptic curves of conductor up to 1000 was computed with Algol68! John On 26/09/2007, Mike Hansen [EMAIL PROTECTED] wrote: No, I don't think you can make custom infix operators in Python. With something as long as 'boxproduct', you'd probably just be better off making it a method. --Mike On 9/26/07, Jason Grout [EMAIL PROTECTED] wrote: Mike Hansen wrote: Since I don't think that graphs and polytopes fall under the SAGE coercion model, overloading operators is pretty straightforward. You just need to define the __add__ method in your class. x + y will call x.__add__(y). sage: class Foo: : def __add__(self, y): : return 42 : sage: a = Foo() sage: b = Foo() sage: a + b 42 sage: b + a 42 Note that you'll want to do some type-checking so that y is what you actually think it should be. Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason -- John Cremona --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On 26-Sep-07, at 10:13 AM, John Cremona wrote: Nostalgia for Algol68 ! The line OP OVER = (INT n,d)RAT: cancel(RAT(n,d)); is taken from my implementation of rational numbers so one half could be written 1 OVER 2 and I could also write DET M The past is the present: haskell supports det m, 1 `over` 2, and ocaml often writes x # foo for foo x. Nick --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On Sep 26, 7:31 am, Jason Grout [EMAIL PROTECTED] wrote: Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason If you're willing to put a lot of effort into the project, you might be able to do something with Logix (http://www.livelogix.net/logix/). From the website: Logix compiles to Python byte-code and can be freely mixed with regular Python modules. ... Logix syntax can be extended on the fly - even at the interactive prompt. Logix is like Lisp with extensible syntax. Or, Logix is Python with syntax extension, expression based syntax, and a powerful macro system. (I've never used Logix myself, only read some of their documentation.) Carl Witty --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
cwitty wrote: On Sep 26, 7:31 am, Jason Grout [EMAIL PROTECTED] wrote: Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason If you're willing to put a lot of effort into the project, you might be able to do something with Logix (http://www.livelogix.net/logix/). From the website: Logix compiles to Python byte-code and can be freely mixed with regular Python modules. ... Logix syntax can be extended on the fly - even at the interactive prompt. Logix is like Lisp with extensible syntax. Or, Logix is Python with syntax extension, expression based syntax, and a powerful macro system. (I've never used Logix myself, only read some of their documentation.) Wow!!! Thanks for sending this link. Logix seems to implement in Python many of the Lisp-like things that make Mathematica such a nice system to use. (like, in this discussion, the ability to define custom infix operators, as well as a syntax for postfix function application, like the Mathematica x//Foo for Foo[x] ). I can't spread myself too thin right now by working on Logix, but I'm bookmarking it. For now, I'll go back to writing code/fixing bugs in the Graph class. -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
It might be worth pointing out that adding new syntax in Mathematica is (usually) done by assignments to the function that transforms general two dimensional input into source code. That isn't really the same thing as adding a new operator to the language itself. On Sep 26, 2:07 pm, Jason Grout [EMAIL PROTECTED] wrote: cwitty wrote: On Sep 26, 7:31 am, Jason Grout [EMAIL PROTECTED] wrote: Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason If you're willing to put a lot of effort into the project, you might be able to do something with Logix (http://www.livelogix.net/logix/). From the website: Logix compiles to Python byte-code and can be freely mixed with regular Python modules. ... Logix syntax can be extended on the fly - even at the interactive prompt. Logix is like Lisp with extensible syntax. Or, Logix is Python with syntax extension, expression based syntax, and a powerful macro system. (I've never used Logix myself, only read some of their documentation.) Wow!!! Thanks for sending this link. Logix seems to implement in Python many of the Lisp-like things that make Mathematica such a nice system to use. (like, in this discussion, the ability to define custom infix operators, as well as a syntax for postfix function application, like the Mathematica x//Foo for Foo[x] ). I can't spread myself too thin right now by working on Logix, but I'm bookmarking it. For now, I'll go back to writing code/fixing bugs in the Graph class. -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Of all the code I've read in lots of different languages, I think code written for Mathematica has been the least transparent. --Mike On 9/26/07, Chris Chiasson [EMAIL PROTECTED] wrote: It might be worth pointing out that adding new syntax in Mathematica is (usually) done by assignments to the function that transforms general two dimensional input into source code. That isn't really the same thing as adding a new operator to the language itself. On Sep 26, 2:07 pm, Jason Grout [EMAIL PROTECTED] wrote: cwitty wrote: On Sep 26, 7:31 am, Jason Grout [EMAIL PROTECTED] wrote: Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason If you're willing to put a lot of effort into the project, you might be able to do something with Logix (http://www.livelogix.net/logix/). From the website: Logix compiles to Python byte-code and can be freely mixed with regular Python modules. ... Logix syntax can be extended on the fly - even at the interactive prompt. Logix is like Lisp with extensible syntax. Or, Logix is Python with syntax extension, expression based syntax, and a powerful macro system. (I've never used Logix myself, only read some of their documentation.) Wow!!! Thanks for sending this link. Logix seems to implement in Python many of the Lisp-like things that make Mathematica such a nice system to use. (like, in this discussion, the ability to define custom infix operators, as well as a syntax for postfix function application, like the Mathematica x//Foo for Foo[x] ). I can't spread myself too thin right now by working on Logix, but I'm bookmarking it. For now, I'll go back to writing code/fixing bugs in the Graph class. -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
The sage coercion model guarantees that when x.__add__(y) is called (well, technically x._add_ or x._add_c_impl), the argument y is of the same type and parent as x. For example sage: R.x = ZZ[] sage: x+1/2 x + 1/2 sage: parent(x) Univariate Polynomial Ring in x over Integer Ring sage: parent(1/2) Rational Field sage: parent(x+1/2) Univariate Polynomial Ring in x over Rational Field In your case, someone could type sage: g = CompleteGraph(5) sage: g+1 # will call g.__add__(1) ??? which you have to deal with. Typically, just throw an error if it's not a graph. But what about graph + digraph? Digraph + digraph? Etc. Should graph + x be a new graph with a new (unconnected) vertex 'x'? - Robert On Sep 25, 2007, at 4:59 PM, Hamptonio wrote: Thanks, now I understand what to do. However, I don't understand your comment about ...the SAGE coercion model. What is an example of that - the sage integers? -M. Hampton On Sep 25, 12:10 pm, Mike Hansen [EMAIL PROTECTED] wrote: Since I don't think that graphs and polytopes fall under the SAGE coercion model, overloading operators is pretty straightforward. You just need to define the __add__ method in your class. x + y will call x.__add__(y). sage: class Foo: : def __add__(self, y): : return 42 : sage: a = Foo() sage: b = Foo() sage: a + b 42 sage: b + a 42 Note that you'll want to do some type-checking so that y is what you actually think it should be. --Mike On 9/25/07, Hamptonio [EMAIL PROTECTED] wrote: I would appreciate any tips on how to extend the + operator in this way, since I would like to implement Minkowski sums of polytopes and this is natural notation for that. Marshall On Sep 25, 10:37 am, Mike Hansen [EMAIL PROTECTED] wrote: In SAGE, '+' is used for union of sets. For example, sage: a = Set([1,2]) sage: b = Set([2,3]) sage: a+b {1, 2, 3} Since currently, + is not defined for graphs, it'd be a natural choice. --Mike On 9/25/07, Jason Grout [EMAIL PROTECTED] wrote: I'm thinking more about how to make the Graph class easy to use. One thing that crops up is that the operations that combine graphs only combine two graphs at a time (e.g., g.union(h), where g and h are graphs). Is there a way to define an infix operator that would allow one to say: g union h union i union j union k? I could do it with something like: reduce(lambda x,y: x.union(y), [g,h,i,j,k]) But that doesn't seem as clear as the infix things above. For reference, Mathematica allows an operator in backticks to be applied to its surrounding arguments, so the equivalent operation above would be: g `union` h `union` i `union` j `union` k And of course, you can set whether the operator is left- associative or right-associative. Of course, one solution is to use a for loop: newgraph=Graph() for graph in [g,h,i,j,k]: newgraph.union(graph) But that seems a lot clunkier than the infix expression above. I guess another solution is to return the new graph from the union, so that you could do: g.union(h).union(i).union(j) Thoughts? -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Robert Bradshaw wrote: The sage coercion model guarantees that when x.__add__(y) is called (well, technically x._add_ or x._add_c_impl), the argument y is of the same type and parent as x. For example sage: R.x = ZZ[] sage: x+1/2 x + 1/2 sage: parent(x) Univariate Polynomial Ring in x over Integer Ring sage: parent(1/2) Rational Field sage: parent(x+1/2) Univariate Polynomial Ring in x over Rational Field In your case, someone could type sage: g = CompleteGraph(5) sage: g+1 # will call g.__add__(1) ??? which you have to deal with. Typically, just throw an error if it's not a graph. But what about graph + digraph? Digraph + digraph? Etc. Should graph + x be a new graph with a new (unconnected) vertex 'x'? Thanks for the explanation of the coercion model. That is a very nice way to do things. As for graphs, there isn't a standard definition for the + operation that is agreed upon, even for graphs of the same type (e.g., should it be disjoint union or union?). That's why I was asking about custom infix operators, so I could make a disjoint_union infix operator, which was clearly different than the union operator, which was clearly different than whatever else. Another reason for custom infix operators was to allow for all sorts of different kinds of products (just look at the Graph class to see many examples), any of which could be represented as graph * graph. The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? With time, I suppose the graph theory community will settle on a commonly accepted meaning for graph + graph, but as far as I understand, that hasn't happened yet. Thanks, -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? I think A.union(B) should leave A and B unchanged and return a new graph object since that's pretty much how everything else works in Python. In-place operations feel very out-of-place ;-) --Mike --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
On Sep 26, 2007, at 8:59 PM, Jason Grout wrote: Robert Bradshaw wrote: The sage coercion model guarantees that when x.__add__(y) is called (well, technically x._add_ or x._add_c_impl), the argument y is of the same type and parent as x. For example sage: R.x = ZZ[] sage: x+1/2 x + 1/2 sage: parent(x) Univariate Polynomial Ring in x over Integer Ring sage: parent(1/2) Rational Field sage: parent(x+1/2) Univariate Polynomial Ring in x over Rational Field In your case, someone could type sage: g = CompleteGraph(5) sage: g+1 # will call g.__add__(1) ??? which you have to deal with. Typically, just throw an error if it's not a graph. But what about graph + digraph? Digraph + digraph? Etc. Should graph + x be a new graph with a new (unconnected) vertex 'x'? Thanks for the explanation of the coercion model. That is a very nice way to do things. It's the product of a lot of work by a lot of people. As for graphs, there isn't a standard definition for the + operation that is agreed upon, even for graphs of the same type (e.g., should it be disjoint union or union?). That's why I was asking about custom infix operators, so I could make a disjoint_union infix operator, which was clearly different than the union operator, which was clearly different than whatever else. Another reason for custom infix operators was to allow for all sorts of different kinds of products (just look at the Graph class to see many examples), any of which could be represented as graph * graph. The same issue came up with vector * vector. It used to be pairwise product, but most people expected dot product, so that was changed. In any case, the old operations should probably be left as methods and __add__ would dispatch to one of them. I think (normal) union is the best way to define it, as graphs are like sets + edges, and for sets + does the normal union thing. I don't think there's a way to make new infix operators, and am not sure that is a good idea anyway. The logic operators of __or__ and __and__ might make sense for union and intersection though. The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) I actually think that this looks very clear, despite the lack of infix operators. where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? I would rather union returns a new object rather than modifying A, it's much easier to reason about code that way, and fits better with the rest of SAGE. One could provide a different method to do union in place if one needs. With time, I suppose the graph theory community will settle on a commonly accepted meaning for graph + graph, but as far as I understand, that hasn't happened yet. Thanks, -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Mike Hansen wrote: Of all the code I've read in lots of different languages, I think code written for Mathematica has been the least transparent. I guess I've always loved functional languages, so the simple core principles appealed to me right away. Some things I love include the consistency in naming (system things always start with a capital letter, leaving anything starting with a lowercase letter to the user; any function asking a boolean question ends in Q, etc), the ability to use postfix, prefix, and infix styles fairly naturally, the compact lambda notation, the functional ideology, etc. However, I don't like the monetary price, for me and for others trying to run my code or experiment with things, and I don't like being tied to a commercial entity. Both of those dislikes are strong enough for me to break with Mathematica when I can and use and publish work using SAGE and contribute where I can. I do admit, though, that you can often express things extremely compactly in Mathematica, which has a tendency to lead to very dense code that isn't very transparent; I see your point. Language issues aside, though, there are some _very_ nice user features in Mathematica, especially the latest version. For example, one small but very useful feature is that any variable that does not have a value attached is a different color in the front end and any variable that is a dummy variable in a command is a third color. Okay, back to the topic of what we can do in Sage and how to make Sage better... Thanks, -Jason --Mike On 9/26/07, Chris Chiasson [EMAIL PROTECTED] wrote: It might be worth pointing out that adding new syntax in Mathematica is (usually) done by assignments to the function that transforms general two dimensional input into source code. That isn't really the same thing as adding a new operator to the language itself. On Sep 26, 2:07 pm, Jason Grout [EMAIL PROTECTED] wrote: cwitty wrote: On Sep 26, 7:31 am, Jason Grout [EMAIL PROTECTED] wrote: Can we define custom infix operators? Suppose I'd like boxproduct to be an infix operator. Could I make that work? Thanks, Jason If you're willing to put a lot of effort into the project, you might be able to do something with Logix (http://www.livelogix.net/logix/). From the website: Logix compiles to Python byte-code and can be freely mixed with regular Python modules. ... Logix syntax can be extended on the fly - even at the interactive prompt. Logix is like Lisp with extensible syntax. Or, Logix is Python with syntax extension, expression based syntax, and a powerful macro system. (I've never used Logix myself, only read some of their documentation.) Wow!!! Thanks for sending this link. Logix seems to implement in Python many of the Lisp-like things that make Mathematica such a nice system to use. (like, in this discussion, the ability to define custom infix operators, as well as a syntax for postfix function application, like the Mathematica x//Foo for Foo[x] ). I can't spread myself too thin right now by working on Logix, but I'm bookmarking it. For now, I'll go back to writing code/fixing bugs in the Graph class. -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
The best I've come up with at this point is, given graphs A,B,C,D,E, the union is: A.union(B).union(C).union(D).union(E) I actually think that this looks very clear, despite the lack of infix operators. I agree -- especially since it looks like a literal translation of the same statement as written mathematics. However, I could see a strong argument that it could get too long -- it wouldn't take much work to change union to support something like this: A.union( [ B, C, D, E ] ) where the union function returns the modified graph each time so I can keep chaining union function calls. The union actually modifies the graph A, so if you want A left alone and a new graph returned, you do: newgraph=A.copy().union(B).union(C).union(D).union(E) Comments? Is there a better way to do things? I would rather union returns a new object rather than modifying A, it's much easier to reason about code that way, and fits better with the rest of SAGE. One could provide a different method to do union in place if one needs. I second Robert's opinion, especially since it makes it feel more like a functional language that way. On a related note, you mentioned functional programming elsewhere in this thread -- python definitely supports some functional programming notions (anonymous functions, first-class functions, etc). Here's a nice link about that: http://www.ibm.com/developerworks/library/l-prog.html -cc --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
In SAGE, '+' is used for union of sets. For example, sage: a = Set([1,2]) sage: b = Set([2,3]) sage: a+b {1, 2, 3} Since currently, + is not defined for graphs, it'd be a natural choice. --Mike On 9/25/07, Jason Grout [EMAIL PROTECTED] wrote: I'm thinking more about how to make the Graph class easy to use. One thing that crops up is that the operations that combine graphs only combine two graphs at a time (e.g., g.union(h), where g and h are graphs). Is there a way to define an infix operator that would allow one to say: g union h union i union j union k? I could do it with something like: reduce(lambda x,y: x.union(y), [g,h,i,j,k]) But that doesn't seem as clear as the infix things above. For reference, Mathematica allows an operator in backticks to be applied to its surrounding arguments, so the equivalent operation above would be: g `union` h `union` i `union` j `union` k And of course, you can set whether the operator is left-associative or right-associative. Of course, one solution is to use a for loop: newgraph=Graph() for graph in [g,h,i,j,k]: newgraph.union(graph) But that seems a lot clunkier than the infix expression above. I guess another solution is to return the new graph from the union, so that you could do: g.union(h).union(i).union(j) Thoughts? -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
I would appreciate any tips on how to extend the + operator in this way, since I would like to implement Minkowski sums of polytopes and this is natural notation for that. Marshall On Sep 25, 10:37 am, Mike Hansen [EMAIL PROTECTED] wrote: In SAGE, '+' is used for union of sets. For example, sage: a = Set([1,2]) sage: b = Set([2,3]) sage: a+b {1, 2, 3} Since currently, + is not defined for graphs, it'd be a natural choice. --Mike On 9/25/07, Jason Grout [EMAIL PROTECTED] wrote: I'm thinking more about how to make the Graph class easy to use. One thing that crops up is that the operations that combine graphs only combine two graphs at a time (e.g., g.union(h), where g and h are graphs). Is there a way to define an infix operator that would allow one to say: g union h union i union j union k? I could do it with something like: reduce(lambda x,y: x.union(y), [g,h,i,j,k]) But that doesn't seem as clear as the infix things above. For reference, Mathematica allows an operator in backticks to be applied to its surrounding arguments, so the equivalent operation above would be: g `union` h `union` i `union` j `union` k And of course, you can set whether the operator is left-associative or right-associative. Of course, one solution is to use a for loop: newgraph=Graph() for graph in [g,h,i,j,k]: newgraph.union(graph) But that seems a lot clunkier than the infix expression above. I guess another solution is to return the new graph from the union, so that you could do: g.union(h).union(i).union(j) Thoughts? -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Mike Hansen wrote: Since I don't think that graphs and polytopes fall under the SAGE coercion model, overloading operators is pretty straightforward. You just need to define the __add__ method in your class. x + y will call x.__add__(y). sage: class Foo: : def __add__(self, y): : return 42 : sage: a = Foo() sage: b = Foo() sage: a + b 42 sage: b + a 42 Note that you'll want to do some type-checking so that y is what you actually think it should be. Thanks! Knowing what to look for, google pulled up the following page from the python website, delineating the overloadable operators: http://docs.python.org/ref/numeric-types.html I put this here for future reference. Thanks, Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---
[sage-devel] Re: making new infix operators
Thanks, now I understand what to do. However, I don't understand your comment about ...the SAGE coercion model. What is an example of that - the sage integers? -M. Hampton On Sep 25, 12:10 pm, Mike Hansen [EMAIL PROTECTED] wrote: Since I don't think that graphs and polytopes fall under the SAGE coercion model, overloading operators is pretty straightforward. You just need to define the __add__ method in your class. x + y will call x.__add__(y). sage: class Foo: : def __add__(self, y): : return 42 : sage: a = Foo() sage: b = Foo() sage: a + b 42 sage: b + a 42 Note that you'll want to do some type-checking so that y is what you actually think it should be. --Mike On 9/25/07, Hamptonio [EMAIL PROTECTED] wrote: I would appreciate any tips on how to extend the + operator in this way, since I would like to implement Minkowski sums of polytopes and this is natural notation for that. Marshall On Sep 25, 10:37 am, Mike Hansen [EMAIL PROTECTED] wrote: In SAGE, '+' is used for union of sets. For example, sage: a = Set([1,2]) sage: b = Set([2,3]) sage: a+b {1, 2, 3} Since currently, + is not defined for graphs, it'd be a natural choice. --Mike On 9/25/07, Jason Grout [EMAIL PROTECTED] wrote: I'm thinking more about how to make the Graph class easy to use. One thing that crops up is that the operations that combine graphs only combine two graphs at a time (e.g., g.union(h), where g and h are graphs). Is there a way to define an infix operator that would allow one to say: g union h union i union j union k? I could do it with something like: reduce(lambda x,y: x.union(y), [g,h,i,j,k]) But that doesn't seem as clear as the infix things above. For reference, Mathematica allows an operator in backticks to be applied to its surrounding arguments, so the equivalent operation above would be: g `union` h `union` i `union` j `union` k And of course, you can set whether the operator is left-associative or right-associative. Of course, one solution is to use a for loop: newgraph=Graph() for graph in [g,h,i,j,k]: newgraph.union(graph) But that seems a lot clunkier than the infix expression above. I guess another solution is to return the new graph from the union, so that you could do: g.union(h).union(i).union(j) Thoughts? -Jason --~--~-~--~~~---~--~~ To post to this group, send email to sage-devel@googlegroups.com To unsubscribe from this group, send email to [EMAIL PROTECTED] For more options, visit this group at http://groups.google.com/group/sage-devel URLs: http://sage.scipy.org/sage/ and http://modular.math.washington.edu/sage/ -~--~~~~--~~--~--~---