pearu / sympycore

Automatically exported from code.google.com/p/sympycore
Other
11 stars 1 forks source link

Unevaluated instances #18

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
Find a way to create and manipulate unevaluated instances.

Options:
- use canonize=False option to class constructors.
- use some global flag
- introduce unevaluated classes, eg UnevaluatedAdd for Add, etc.

Possible problems:
- default evaluation introduces some assumptions to
  the internal representation of a symbolic instance.
  For example, in Add(2,3,x) the numbers `2` and `3` are added
  and internal reprsentation of the expression becomes
      Add({1: 5, x: 1})
  that is keys equal to `1` contain numbers.
  What should be the internal representation
      Add(2,3,x, canonize=False)
  ? Add({2:1,3:1,x:1})? To take this into account, codes
  become more verbose because the assumption that 
  only the item with key `1` contains numbers is not 
  valid.

Original issue reported on code.google.com by pearu.peterson on 30 Oct 2007 at 9:08

GoogleCodeExporter commented 9 years ago
Inspired by the latest post to sympylist. :) 

I, as the user, I don't see why I would need this feature and why I should care 
about
the internal representation. Why do you think it's useful?

Original comment by ondrej.c...@gmail.com on 30 Oct 2007 at 10:08

GoogleCodeExporter commented 9 years ago
Indeed, this issue is raised from users feedback. The last one is from 
sympylist.

The first one comes from Simula where people typically use pyginac and
deal with huge but simple expression trees that can easily take all
the memory that computer has. Say, adding two huge trees would create
a third huge tree.
 a = 2 + ...
 b = 3 + ...
 a + b -> 5 + ... + ...

A typical task with these expressions is to
generate C/C++ code with minimal number of operations.
Therefore, it is sometimes preferable not to carry out
elementary simplifications but keep original expressions in
an Add instance:

 a + b -> (2 + ...) + (3 + ...)

and that takes almost no memory.

Original comment by pearu.peterson on 30 Oct 2007 at 10:27

GoogleCodeExporter commented 9 years ago
So is it a question of memory intensive Add.canonize()?

Because simplifying the expression usually reduces the number of terms.

Original comment by ondrej.c...@gmail.com on 30 Oct 2007 at 10:44

GoogleCodeExporter commented 9 years ago
No. Consider

 s1 = 2 + a + b
 s2 = 3 + c + d

s1 + s2 -> 5 + a + b + c + d

Original comment by pearu.peterson on 30 Oct 2007 at 10:48

GoogleCodeExporter commented 9 years ago
Compared to:

 s1 = 2 + a + b
 s2 = 3 + c + d

s1 + s2 -> 2 + 3 + a + b + c + d

?

I am missing the point.

Original comment by ondrej.c...@gmail.com on 30 Oct 2007 at 10:59

GoogleCodeExporter commented 9 years ago
This should be compared to unevaluated sum:

s1 + s2 -> s = (2 + a + b) + (3 + c + d)

where the following properties hold:
 len(s)==2
 s[0]==s1
 s[1]==s2

Original comment by pearu.peterson on 30 Oct 2007 at 11:02

GoogleCodeExporter commented 9 years ago
ok. So the properties of

 s1 = 2 + a + b
 s2 = 3 + c + d

s1 + s2 -> s3=5 + a + b + c + d

are:

len(s3) = 5
s3[0] = 5
s3[1] = a
...

It seems to me that s3 consumes less memory than s.

Original comment by ondrej.c...@gmail.com on 30 Oct 2007 at 11:16

GoogleCodeExporter commented 9 years ago
Ah, you mean the memory of "s1+s2+s" as compared to "s1+s2+s3"? Yeah, the sum 
of all
those are indeed better for the s1+s2+s case, since it's reusing the memory of 
s1,
s2. Is this the point?

Original comment by ondrej.c...@gmail.com on 30 Oct 2007 at 11:18

GoogleCodeExporter commented 9 years ago
Yes, the point is not to create no additional huge
expressions but to reuse the pointers of these huge expressions
to form a sum.

Original comment by pearu.peterson on 30 Oct 2007 at 12:17

GoogleCodeExporter commented 9 years ago
One one hand, I think this feature should somehow be supported in SymPy. On the 
other
hand, symbolic arithmetic without simplifications is trivial to implement 
Lisp-style
in Python:

class sym(tuple):
    def __add__(self, other): return sym((self, '+', other))
    def __radd__(self, other): return sym((other, '+', self))
    def __mul__(self, other): return sym((self, '*', other))
    def __rmul__(self, other): return sym((other, '*', self))
    def __repr__(self):
        if len(self) == 1:
            return str(self[0])
        return "(%r %s %r)" % self[:]

x = sym('x')
y = sym('y')
x + 3*(x+y) + 5*y

(You might want to switch to a prefix representation to support more complicated
operations.)

It could easily be implemented in a stand-alone module.

Original comment by fredrik....@gmail.com on 30 Oct 2007 at 12:53

GoogleCodeExporter commented 9 years ago
Another idea would be to introduce parametric symbols (I think ginac has
something similar):

class ParametricSymbol(Symbol):

  def __new__(cls, name, parameter):
      obj = Symbol.__new__(cls, name)
      obj.parameter = parameter
      return obj
  def expand(self):
      return self.parameter.expand()

s1 = Symbol('s1',2+a+b)
s2 = Symbol('s2',3+c+d)
s = s1 + s2

Original comment by pearu.peterson on 30 Oct 2007 at 1:01

GoogleCodeExporter commented 9 years ago
For me, unevaluated lisp-style symbolic trees could be useful, when preparing 
lecture materials, slides, etc.

e.g. if I want to show the simplification process itsel like:

1 + 2 = 3
4 + 9 = 13
1 + 6 = 7
...

or

first_term + second_term = (first+second evaluated)

then I'll use something like constructing an expression via usual syntax like

Basic.canonize_push(False)  # XXX ugly
e = e1 + e2

print e, '=', e.canonize()

Original comment by kirill.s...@gmail.com on 30 Oct 2007 at 5:42

GoogleCodeExporter commented 9 years ago
If I've misunderstood the purpose of unevalulated instances as Pearu proposed, 
please do ignore this comment. But....

In Maple, I could do:

a:= b+c;
b:= 1;
c:= 2;

and then evaluate a to find it's equal to 3. *Then*, I could type:

b:= x;
c:= y;

and evaluate a to find it's now equal to (x+y).

Do you consider this feature of Maple, Mathematica, YACCAs etc to be related to 
these 'unevaluated instances'?

This is a feature I would like to have: the ability to express a mathematical 
relationship which persists as other values change, *without* the need to 
define a 
function each time (i.e. without having to replace 'a' in my example with a 
function) into a function each time. 

I'm used to this from other computer algebra systems and it would certainly be 
useful for my work.

- Has this been considered for Sympy at all?
- Is it too unPythonic?
- If unevaluated instances are implemented, would / could this be supported by 
them?

Many thanks for any reply.

Best regards,
andy

Original comment by and...@hotmail.com on 31 Oct 2007 at 12:56

GoogleCodeExporter commented 9 years ago
Python itself does not support idioms like Maple above.
Also, in sympy all symbolic objects has to be immutable objects.
So, the answer to this proposal is pretty much just 'not-possible'.

Original comment by pearu.peterson on 31 Oct 2007 at 1:20

GoogleCodeExporter commented 9 years ago
Pearu,

Thanks for your reply....

Having thought about it, I can probably obtain what I want by using functions 
with 
zero arguments. It's much simpler than a full function with an argument for 
each of 
the variables in the equation :-). Also, the up-to-date values of the variables 
within the expression will get pulled in through Python's scoping rules. 

There's an example below for simple addition.

(I haven't had time to see if this works with sympy functions and sympy Lambdas 
yet -
 as I don't have access to a machine I can load sympy on today.....)

Best regards,
andy

Python 2.2.3 (#1, Aug  8 2003, 08:44:27)
[GCC 3.2.3 20030502 (Red Hat Linux 3.2.3-13)] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> 
>>> def a():
...     return c + b
...
>>> b=2
>>> c=4
>>> a()
6
>>> b=5
>>> a()
9
>>> c='f'
>>> b='g'
>>> a()
'fg'

Original comment by and...@hotmail.com on 2 Nov 2007 at 1:24

GoogleCodeExporter commented 9 years ago
Nice idea. Here is an extension of it (this requires sympycore):

>>> from sympy import *
>>> def a(locals=locals()): return sympify('c+d',locals=locals)
... 
>>> a()
c + d
>>> c=4
>>> a()
4 + d
>>> c=5
>>> a()
5 + d
>>> c='c'
>>> a()
c + d

Original comment by pearu.peterson on 2 Nov 2007 at 1:31

GoogleCodeExporter commented 9 years ago
See also releated thread in sympy mailing list:
  http://groups.google.com/group/sympy/t/454584a2bea22672

The question of having noun and verb forms of operations
seems relevant. Currently we have only classes of verb kind.
To have also noun forms, there seems two options: (i) add
a flag `canonize=True` to operation constructors or (ii) derive
verb type of classes from noun type of classes by overwriting 
their canonize() methods (that simply return None) with canonize()
methods that performs expression canonization operations.

Here follows a proposed naming convention (in case noun kind
of classes will be implemented):
<VerbClass> - <NounClass>
Add - Addition
Mul - Multiplication
Pow - Exponentation
Sin - Sine
Cos - Cosine
Tan - Tangent
Union - ??
Intersection - ??
etc.

The options (i) could be useful in algorithms that produce input
to operation classes in canonical form and using canonize=False
would avoid unnecessary computation. However, other codes may
assume that an operation instance is in a canonical form and
take an advantage of it - this might introduce incorrect results
when this assumptions turns out to be invalid. So, (ii) option
seems more robust.

Original comment by pearu.peterson on 15 Nov 2007 at 6:09