Hello,

I have a question about best practices in subclassing sympy. If there is a 
good tutorial out there, I would be grateful for a direction, but I could 
not find anything myself. The explanation of what I'm trying to achieve is 
somewhat lengthy. 

I would like to use sympy to declare differential equations that are later 
fed to a numerical solver. To simplify things for users, I want to have two 
kinds of objects:

Dimension:
- behaves like a Symbol with respect to sympy algorithms and printing
- carries additional attributes (in particular, the parameters of the 
associated grid)
- the equality for these objects is checked based on the name _and_ the 
attributes

For instance, one would define
x = Dimension('x', 0, 1, 50) # a dimension 'x' with the grid of 50 points 
on [0, 1]
y = Dimension('y', 0, 1, 50)
x2 = Dimension('x', 0, 1, 20)
xp = Dimension('x', 0, 1, 50)

For all sympy algorithms the relation between `x` and `y` (or `x2`) is the 
same as the relation between `Symbol('x')` and `Symbol('y')` (or 
`Symbol('x2')`), and the relation between `x` and `xp` is the same as the 
relation between `Symbol('x')` and `Symbol('x')` (that is, they're the same 
object).

Field:
- is initialized with a list of Dimension objects, which denote its 
"native" dimensions (similarly to how one would write "f = f(x, y, t)" in a 
paper)
- in sympy expressions behaves just as an undefined function called with 
its native dimensions

That is, if we have `f = Field('f', x, y)`, we can write, say `(f + 
x).diff(x)` which will be equivalent to `(f(x, y) + x).diff(x)`

- the equality is checked based on the name _and_ the list of native 
dimensions 
- it can be applied like a Function object; the resulting object still 
carries around the original object's set of native dimensions

In other words, if I have `f = Field('f', x, y)`, I can use `f(xp, y)` in 
an expression, but I should be able to obtain the references to `x` and `y` 
from it during the traversal.

- during the application some arbitrary error-checking may be executed (I 
just want to be able to intercept such an event, for example, to check that 
integer dimensions are used for integer dimensions etc)
- optionally, a field not applied to anything, or applied to its "native" 
dimensions is printed just as its name


I was able to implement a Dimension object (based on the implementation of 
Symbol) as

import numpy
from sympy import Symbol
from sympy.core.cache import cacheit

class Dimension(Symbol):

    def __new_stage2__(cls, name, start, stop, points):
        obj = super(Dimension, cls).__xnew__(cls, name, real=True)
        obj.params = (start, stop, points)
        obj.grid = numpy.linspace(start, stop, points, endpoint=False)
        return obj

    def __new__(cls, name, *args, **kwds):
        obj = Dimension.__xnew_cached_(cls, name, *args, **kwds)
        return obj
   
    __xnew__ = staticmethod(__new_stage2__)
    __xnew_cached_ = staticmethod(cacheit(__new_stage2__))

    def _hashable_content(self):
        return (Symbol._hashable_content(self), self.params)

which seems to behave in the required way. I am still unclear how to 
implement Field properly: should I subclass Function, or AppliedUndef, or 
something else? Or am I doing something completely incompatible with 
sympy's design?

Thank you in advance.

-- 
You received this message because you are subscribed to the Google Groups 
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email 
to [email protected].
To post to this group, send email to [email protected].
Visit this group at https://groups.google.com/group/sympy.
To view this discussion on the web visit 
https://groups.google.com/d/msgid/sympy/4daad849-0456-4a85-9faf-fe930d75f161%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Reply via email to