Assumptions

199 views
Skip to first unread message

Aaron S. Meurer

unread,
Apr 29, 2011, 6:16:51 PM4/29/11
to sy...@googlegroups.com
So Ondřej, Christian, and I talked about this before GSoC, but I forgot to mention it on the list. We need to figure out, as a community, how to remove the old assumptions system and merge in the new one. We have already had several branches attempting to do it, and we couldn't even get a GSoC to do it because we don't really have a clear strategy on how to do it.

So I think what we need to do is edit this wiki page (https://github.com/sympy/sympy/wiki/Assumptions) and under the "Approaches" section, but your ideas on how to do it, along with a list of pros and cons, like

Idea:
Describe the idea.

Pros:
List the pros.

Cons:
List the cons.

And edit each idea with the pros and cons that you see with it. And then from there hopefully we will be able to see which way will be the best, or at least it will be easier to make a decision about it.

By the way, the examples on that page need to be removed, or refactored to fit the above form. The page was hijacked from something else, so probably they should just be removed.

Aaron Meurer

Ronan Lamy

unread,
Apr 29, 2011, 11:15:25 PM4/29/11
to sy...@googlegroups.com
Le vendredi 29 avril 2011 à 16:16 -0600, Aaron S. Meurer a écrit :
> So Ondřej, Christian, and I talked about this before GSoC, but I forgot
> to mention it on the list. We need to figure out, as a community, how
> to remove the old assumptions system and merge in the new one. We
> have already had several branches attempting to do it, and we couldn't
> even get a GSoC to do it because we don't really have a clear strategy
> on how to do it.

I don't have a strategy, but I have branches in various states of
readiness that attempt to refactor both the old and new assumptions to
make their code simpler and to bring them closer together. Some of the
things I did or tried to do:

* Remove the cumbersome and confusing Assume() syntax.
* Have old-style assumptions defined only for instances of Expr and,
most importantly, not for Booleans.
* Merge sympy.core.logic and sympy.logic.boolalg (mostly by removing the
former).

I'll try to refine some of this into pull requests in the next days.

Tom Bachmann

unread,
Apr 30, 2011, 3:44:10 AM4/30/11
to sympy
On 29 Apr., 23:16, "Aaron S. Meurer" <asmeu...@gmail.com> wrote:
> So Ondřej, Christian, and I talked about this before GSoC, but I forgot to mention it on the list.  We need to figure out, as a community, how to remove the old assumptions system and merge in the new one.  We have already had several branches attempting to do it, and we couldn't even get a GSoC to do it because we don't really have a clear strategy on how to do it.
>

Can you describe what went wrong with these branches? In particular,
why can the following naive strategy not work:

1. Make the new system at least as good as the old one. That is
whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
get at least as good an answer. In particular Symbol('x',
positive=True) should register automatically in the global assumptions
that x > 0 (if I understand correctly how the new system works...).
2. Replace all queries foo.is_bar by ask(foo, Q.bar).
3. Remove all implementations of the is_bar properties.
4. Remove all remaining remnants of the old system.

Vinzent Steinberg

unread,
Apr 30, 2011, 9:26:47 AM4/30/11
to sympy
On Apr 30, 9:44 am, Tom Bachmann <ness...@googlemail.com> wrote:
> On 29 Apr., 23:16, "Aaron S. Meurer" <asmeu...@gmail.com> wrote:
>
> > So Ondřej, Christian, and I talked about this before GSoC, but I forgot to mention it on the list.  We need to figure out, as a community, how to remove the old assumptions system and merge in the new one.  We have already had several branches attempting to do it, and we couldn't even get a GSoC to do it because we don't really have a clear strategy on how to do it.
>
> Can you describe what went wrong with these branches? In particular,
> why can the following naive strategy not work:
>
> 1. Make the new system at least as good as the old one. That is
> whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
> get at least as good an answer.

I think the only problem could be that ask() is much slower for
trivial queries, needs to be verified. This should be not too hard to
fix though.

> In particular Symbol('x',
> positive=True) should register automatically in the global assumptions
> that x > 0 (if I understand correctly how the new system works...).

That is is not that easy, because this approach does not work with
global assumptions. Symbol('x', positive) only affects local symbols,
global assumptions affect *all* symbols.

In other words, you have to manually clean the global assumptions, so
this can't be used as a drop-in replacement for Symbol('x',
positive=True). Imagine running the test suite and global_assumptions
being spammed with hundreds of assumptions.

A solution would be to implement local assumptions, imitating Python's
local scoping. I tried this approach some time ago, it is quite
hackish, but I got it working for Symbol(), but not really for
symbols() or var(). See my local_assump branch.

> 2. Replace all queries foo.is_bar by ask(foo, Q.bar).

I we fix the two things I mentioned above, we could still have
foo.is_bar as a (maybe deprecated) shortcut.

> 3. Remove all implementations of the is_bar properties.
> 4. Remove all remaining remnants of the old system.

Vinzent

Tom Bachmann

unread,
Apr 30, 2011, 10:25:00 AM4/30/11
to sympy

> > 1. Make the new system at least as good as the old one. That is
> > whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
> > get at least as good an answer.
>
> I think the only problem could be that ask() is much slower for
> trivial queries, needs to be verified. This should be not too hard to
> fix though.
>
> > In particular Symbol('x',
> > positive=True) should register automatically in the global assumptions
> > that x > 0 (if I understand correctly how the new system works...).
>
> That is is not that easy, because this approach does not work with
> global assumptions. Symbol('x', positive) only affects local symbols,
> global assumptions affect *all* symbols.
>
> In other words, you have to manually clean the global assumptions, so
> this can't be used as a drop-in replacement for Symbol('x',
> positive=True). Imagine running the test suite and global_assumptions
> being spammed with hundreds of assumptions.
>

Couldn't symbol.__del__ just remove all global assumptions involving
the about-to-be-deleted symbol? I suppose assumptions would have to
store weak references for this to work.

Note: I come from a background of explicit memory allocation, so this
might either not be feasible or not pythonic.

> A solution would be to implement local assumptions, imitating Python's
> local scoping. I tried this approach some time ago, it is quite
> hackish, but I got it working for Symbol(), but not really for
> symbols() or var(). See my local_assump branch.
>

That would of course still be handy to have.

Tom Bachmann

unread,
Apr 30, 2011, 11:59:47 AM4/30/11
to sympy
This is roughly what I had in mind:
[Everything is fake here, to only thing that is supposed to be
illustrated is how assumptions get cleared up when their objects
vanish.]

import weakref

class Assumption(object):
pass

class AssumePositive(Assumption):

def __init__(self, x):
self.var = weakref.ref(x, self.cb)

def cb(self, var):
if self.callback:
self.callback(self)

def __str__(self):
return str(self.var) + ' > 0'

class Symbol(object):
def __init__(self, name):
self.name = name

def __str__(self):
return str(self.name)

assumptions = {}

def remove_assumption(wr):
print 'removing assumption', assumptions[wr]
del assumptions[wr]

N = 0
def assume(a):
global N
print 'adding assumption', a, 'as', N
assumptions[a] = N
a.callback = remove_assumption
N += 1

def test2():
x = Symbol('y')
assume(AssumePositive(x))
print assumptions
print 'leaving test2'

def test():
x = Symbol('x')
assume(AssumePositive(x))
print assumptions
print 'entering test2'
test2()
print assumptions
print 'leaving test'

test()

Ronan Lamy

unread,
Apr 30, 2011, 1:07:27 PM4/30/11
to sy...@googlegroups.com
I don't see how this is in any way better than storing the assumptions
on the object. For instance, assume(AssumePositive(x + y)) won't work
properly. And it's not clear how assumptions can be retrieved.

Haz

unread,
Apr 30, 2011, 1:36:36 PM4/30/11
to sy...@googlegroups.com
So a few things to respond to...

Tom:
>Can you describe what went wrong with these branches? In particular,
>why can the following naive strategy not work:
>
>1. Make the new system at least as good as the old one. That is
>whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
>get at least as good an answer. In particular Symbol('x',

>positive=True) should register automatically in the global assumptions
>that x > 0 (if I understand correctly how the new system works...).
>2. Replace all queries foo.is_bar by ask(foo, Q.bar).
>3. Remove all implementations of the is_bar properties.
>4. Remove all remaining remnants of the old system.

  This is what was largely tried with a branch last summer:

  There was push-back from the community since removing the Symbol('x', positive=True) syntax was largely frowned upon.

Vinzent:
>I think the only problem could be that ask() is much slower for
>trivial queries, needs to be verified. This should be not too hard to
>fix though.

  I don't think this is the case. Trivial queries (those that already have obvious implications) are compiled into quick reference dictionaries. In particular:

-----

  In the end, everything in the new assumption system is done with a particular assumption context -- design decisions must be made on which context is used in the ambiguous cases. There is also an issue (feature request) that makes things more natural that will be worked on after the assumptions shift has taken place -- things like the following would be possible:

with <some assumptions on variables>:
  <do stuff>

  This gives the assumptions an explicit local scope. The branch from last summer was mostly filled with fixing test cases so they used the new assumption system (with the global assumptions), and then cleaned up after themselves.

  It should also be emphasized that it isn't just a simple notion of cleaning up assumptions after yourself -- the cache interacts in quite an intertwined way. Under the right assumptions, expressions will be simplified, and variables will be assigned certain values. If that's stored in the cache, changing any related assumption will invalidate the result. For this reason, I had to manually clear the cache on a regular basis. I think that the cache is only really needed for lengthy solve algorithms (so you don't recompute an expression over and over), in which case we can probably wipe it after any sympy command is invoked (i.e., only use the cache while sympy is computing things, and then flush it just before returning the result). However, the branches from last summer never made it this far.

  I think a few things are generally universally accepted (correct me if I'm wrong though):
1. sympy/core/assumptions.py should be removed, and everything patched up to use sympy/assumptions/*
2. sympy/core/logic.py should be removed and replaced with sympy/logic/*
3. Assumptions should probably have the right references to be properly deleted from a context when their symbol is deleted -- this goes for any AssumptionContext (not just the global one)

  Open questions that need to be hashed out:
4. Do we remove the Symbol('x', positive=True) syntax?
5. If not, where does the assumption that x is positive get placed? (locally / globally)
6. How do we reconcile the issues with the cache remembering computations under certain assumptions? (flush after sympy computations / flush after an assumption changes / etc)

  Am I missing anything?

  Ronan: Can you elaborate on this? (my memory is failing me at the moment)
> * Remove the cumbersome and confusing Assume() syntax

  Cheers


--
You received this message because you are subscribed to the Google Groups "sympy" group.
To post to this group, send email to sy...@googlegroups.com.
To unsubscribe from this group, send email to sympy+un...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/sympy?hl=en.


Tom Bachmann

unread,
Apr 30, 2011, 2:16:15 PM4/30/11
to sympy
I was just making a suggestion on how to implement certain
functionality with the assumptions implemented, not arguing why.
[Actually I don't see why one would want to separate out assumptions
in the first place, but I suppose there are good reasons]. As for
things like AssumePositive(x+y) you are right, but this is just
because my example implementation is simplistic. One way around this
would be to hijack all free symbols inside an assumption into week
references, something like

class Assumption(object)
def __init__(expr):
# This object means that EXPR is true.
sym = expr.free_symbols
for x in s:
expr = expr.subs(x, weakref.ref(x))

As for retrieving assumptions, I don't know, before posting my
previous mail I just had a quick look at assumptions/assume.py and it
seemed to me that assumptions are simply stored as a set. I don't know
how the assumptions system works so I cannot say anything more.

Tom Bachmann

unread,
Apr 30, 2011, 2:21:58 PM4/30/11
to sympy

>   It should also be emphasized that it isn't just a simple notion of
> cleaning up assumptions after yourself -- the cache interacts in quite an
> intertwined way. Under the right assumptions, expressions will be
> simplified, and variables will be assigned certain values. If that's stored
> in the cache, changing any related assumption will invalidate the result.

Why not make assumptions part of the hash of an object? At least to
the extent that the cache is concerned?

Aaron S. Meurer

unread,
Apr 30, 2011, 2:26:00 PM4/30/11
to sy...@googlegroups.com
I don't think that would work if we have assumptions that are separate from the objects themselves. In other words, if the assumptions change, then the hash would have to change.

Aaron Meurer

Aaron S. Meurer

unread,
Apr 30, 2011, 2:31:38 PM4/30/11
to sy...@googlegroups.com
I think using __del__ is a bad idea. See http://docs.python.org/reference/datamodel.html?highlight=__del__#object.__del__. Basically, __del__ is *not* called when you do "del x". First off, "del x" reduces the reference count of x by 1, whereas x.__del__ is only called if the reference count reaches zero. So if you do

a = [x]
del x

x.__del__ won't be called, because there is still a reference to x in b. Second, I think that __del__ is not guaranteed to happen immediately, but only at the next garbage collection event (I might be wrong here, though). But I think that __del__ should only be used for cleanup magic that doesn't happen automatically with the regular garbage collector. Implementing a feature like assumptions in it isn't what it was made for.

By the way, Ronan, Vinzent, Christian, and Tom, can you all add the ideas you have mentioned here as ideas on that wiki page?

Aaron Meurer

Tom Bachmann

unread,
Apr 30, 2011, 2:38:22 PM4/30/11
to sympy
On 30 Apr., 19:26, "Aaron S. Meurer" <asmeu...@gmail.com> wrote:
> I don't think that would work if we have assumptions that are separate from the objects themselves.  In other words, if the assumptions change, then the hash would have to change.
>

Well the hashable_content method (or whatever it is called) could
query for any currently active assumptions and amalgamate them
somehow. Of course this gets fairly close to having the assumptions
inside the objects again.


> I think using __del__ is a bad idea. See http://docs.python.org/reference/datamodel.html?highlight=__del__#obj.... Basically, __del__ is *not* called when you do "del x". First off, "del > x" reduces the reference count of x by 1, whereas x.__del__ is only called if the reference count reaches zero. So if you do
>
> a = [x]
> del x
>
> x.__del__ won't be called, because there is still a reference to x in b. Second, I think that __del__ is not guaranteed to happen immediately, but only at the next garbage collection event > (I might be wrong here, though). But I think that __del__ should only be used for cleanup magic that doesn't happen automatically with the regular garbage collector. Implementing a feature like assumptions in it isn't what it was made for.

But isn't this exactly what we are doing? There is no problem with
having global assumptions about unused objects, other than that it is
inefficient. So essentially we are trying to teach the garbage
collector that objects referenced only by assumptions can be recycled.

> By the way, Ronan, Vinzent, Christian, and Tom, can you all add the ideas you have mentioned here as ideas on that wiki page?

Will do.

Ronan Lamy

unread,
Apr 30, 2011, 3:24:45 PM4/30/11
to sy...@googlegroups.com
Le samedi 30 avril 2011 à 13:36 -0400, Haz a écrit :
> So a few things to respond to...
>
>
> Tom:
> >Can you describe what went wrong with these branches? In particular,
> >why can the following naive strategy not work:
> >
> >1. Make the new system at least as good as the old one. That is
> >whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I
> will
> >get at least as good an answer. In particular Symbol('x',
> >positive=True) should register automatically in the global
> assumptions
> >that x > 0 (if I understand correctly how the new system works...).
> >2. Replace all queries foo.is_bar by ask(foo, Q.bar).
> >3. Remove all implementations of the is_bar properties.
> >4. Remove all remaining remnants of the old system.
>
>
> This is what was largely tried with a branch last summer:
> - https://github.com/haz/sympy/tree/disconnect-assumptions-2
>
>
> There was push-back from the community since removing the
> Symbol('x', positive=True) syntax was largely frowned upon.

As I recall, the problems were that it was slow and broke a lot of
things. Symbol(x, positive=True) is easy to hack back in.

>
> Vinzent:
> >I think the only problem could be that ask() is much slower for
> >trivial queries, needs to be verified. This should be not too hard to
> >fix though.
>
>
> I don't think this is the case. Trivial queries (those that already
> have obvious implications) are compiled into quick reference
> dictionaries. In particular:
> - https://github.com/sympy/sympy/blob/master/sympy/assumptions/ask.py#L114
>

I think that the problem is with non-trivial queries, for which that
code does nothing.

>
> In the end, everything in the new assumption system is done with a
> particular assumption context -- design decisions must be made on
> which context is used in the ambiguous cases. There is also an issue
> (feature request) that makes things more natural that will be worked
> on after the assumptions shift has taken place -- things like the
> following would be possible:
>
>
> with <some assumptions on variables>:
> <do stuff>
>
>
> This gives the assumptions an explicit local scope. The branch from
> last summer was mostly filled with fixing test cases so they used the
> new assumption system (with the global assumptions), and then cleaned
> up after themselves.
>
>
> It should also be emphasized that it isn't just a simple notion of
> cleaning up assumptions after yourself -- the cache interacts in quite
> an intertwined way. Under the right assumptions, expressions will be
> simplified, and variables will be assigned certain values. If that's
> stored in the cache, changing any related assumption will invalidate
> the result. For this reason, I had to manually clear the cache on a
> regular basis. I think that the cache is only really needed for
> lengthy solve algorithms (so you don't recompute an expression over
> and over), in which case we can probably wipe it after any sympy
> command is invoked (i.e., only use the cache while sympy is computing
> things, and then flush it just before returning the result). However,
> the branches from last summer never made it this far.
>

The cache is the source of a lot of problems. We should try to use it as
little as possible, even if it's not really possible to remove it at
this time.

>
> I think a few things are generally universally accepted (correct me
> if I'm wrong though):
> 1. sympy/core/assumptions.py should be removed, and everything patched
> up to use sympy/assumptions/*
> 2. sympy/core/logic.py should be removed and replaced with
> sympy/logic/*
> 3. Assumptions should probably have the right references to be
> properly deleted from a context when their symbol is deleted -- this
> goes for any AssumptionContext (not just the global one)
>

I'm not sure about 1: sympy/core/assumptions.py and sympy/assumptions/*
should be merged, but I don't know how exactly the merge should happen.

>
> Ronan: Can you elaborate on this? (my memory is failing me at the
> moment)
> > * Remove the cumbersome and confusing Assume() syntax

The idea is to write '~Q.real(x)' instead of 'Assume(x, Q.real, False)'.
It's shorter, easier to understand and doesn't suggest wrongly that
creating the object does anything beyond that.


Aaron S. Meurer

unread,
Apr 30, 2011, 6:41:02 PM4/30/11
to sy...@googlegroups.com

If a specific algorithm is slow without the cache, I think it should be rewritten to not do duplicate calculations.

>
>>
>> I think a few things are generally universally accepted (correct me
>> if I'm wrong though):
>> 1. sympy/core/assumptions.py should be removed, and everything patched
>> up to use sympy/assumptions/*
>> 2. sympy/core/logic.py should be removed and replaced with
>> sympy/logic/*
>> 3. Assumptions should probably have the right references to be
>> properly deleted from a context when their symbol is deleted -- this
>> goes for any AssumptionContext (not just the global one)
>>
> I'm not sure about 1: sympy/core/assumptions.py and sympy/assumptions/*
> should be merged, but I don't know how exactly the merge should happen.
>
>>
>> Ronan: Can you elaborate on this? (my memory is failing me at the
>> moment)
>>> * Remove the cumbersome and confusing Assume() syntax
>
> The idea is to write '~Q.real(x)' instead of 'Assume(x, Q.real, False)'.
> It's shorter, easier to understand and doesn't suggest wrongly that
> creating the object does anything beyond that.
>

+1. And I agree that Assume() can be confusing (especially considering that Maple's assume() actually does create global assumptions, for example).

Aaron Meurer

Vinzent Steinberg

unread,
Apr 30, 2011, 7:20:45 PM4/30/11
to sympy
On Apr 30, 7:36 pm, Haz <christian.mu...@gmail.com> wrote:
> >I think the only problem could be that ask() is much slower for
> >trivial queries, needs to be verified. This should be not too hard to
> >fix though.
>
>   I don't think this is the case. Trivial queries (those that already have
> obvious implications) are compiled into quick reference dictionaries. In
> particular:
> -https://github.com/sympy/sympy/blob/master/sympy/assumptions/ask.py#L114

Thanks for clarifying. I made a quick unrepresentative benchmark:

In [1]: x = Symbol('x', positive=True)

In [2]: global_assumptions.add(Assume(x, Q.positive))

In [3]: %timeit x.is_positive
1000000 loops, best of 3: 418 ns per loop

In [4]: %timeit ask(x, Q.positive)
10000 loops, best of 3: 97.3 us per loop


I think this is fast enough. (Even if it is about 200 times slower.)

Vinzent

Aaron S. Meurer

unread,
Apr 30, 2011, 9:38:46 PM4/30/11
to sy...@googlegroups.com
By the way, the discussion at this issue is relevant: http://code.google.com/p/sympy/issues/detail?id=1884

Aaron Meurer

Ondrej Certik

unread,
May 1, 2011, 2:47:31 AM5/1/11
to sy...@googlegroups.com
On Sat, Apr 30, 2011 at 10:36 AM, Haz <christi...@gmail.com> wrote:
> So a few things to respond to...
> Tom:
>>Can you describe what went wrong with these branches? In particular,
>>why can the following naive strategy not work:
>>
>>1. Make the new system at least as good as the old one. That is
>>whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
>>get at least as good an answer. In particular Symbol('x',
>>positive=True) should register automatically in the global assumptions
>>that x > 0 (if I understand correctly how the new system works...).
>>2. Replace all queries foo.is_bar by ask(foo, Q.bar).
>>3. Remove all implementations of the is_bar properties.
>>4. Remove all remaining remnants of the old system.
>   This is what was largely tried with a branch last summer:
> - https://github.com/haz/sympy/tree/disconnect-assumptions-2
>   There was push-back from the community since removing the Symbol('x',
> positive=True) syntax was largely frowned upon.

I am still very much convinced, that this disconnect-assumptions-2 is
the simplest and easiest way to get rid of the old assumptions, so
that we can start speeding up the core, and start using some other
system for them.

I would be interested in the community vote on this idea. I vote +1. I
am aware that Ronan voted -1 last year. What do others think?

Ondrej

Tom Bachmann

unread,
May 1, 2011, 4:42:48 AM5/1/11
to sympy
FWIW, I think vinzent's approach (local assumptions automatically
injected into the context of the symbol creation) is the best way to
go.

On 1 Mai, 07:47, Ondrej Certik <ond...@certik.cz> wrote:

Ronan Lamy

unread,
May 1, 2011, 10:43:22 AM5/1/11
to sy...@googlegroups.com
Le samedi 30 avril 2011 à 23:47 -0700, Ondrej Certik a écrit :
> On Sat, Apr 30, 2011 at 10:36 AM, Haz <christi...@gmail.com> wrote:
> > So a few things to respond to...
> > Tom:
> >>Can you describe what went wrong with these branches? In particular,
> >>why can the following naive strategy not work:
> >>
> >>1. Make the new system at least as good as the old one. That is
> >>whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
> >>get at least as good an answer. In particular Symbol('x',
> >>positive=True) should register automatically in the global assumptions
> >>that x > 0 (if I understand correctly how the new system works...).
> >>2. Replace all queries foo.is_bar by ask(foo, Q.bar).
> >>3. Remove all implementations of the is_bar properties.
> >>4. Remove all remaining remnants of the old system.
> > This is what was largely tried with a branch last summer:
> > - https://github.com/haz/sympy/tree/disconnect-assumptions-2
> > There was push-back from the community since removing the Symbol('x',
> > positive=True) syntax was largely frowned upon.
>
> I am still very much convinced, that this disconnect-assumptions-2 is
> the simplest and easiest way to get rid of the old assumptions, so
> that we can start speeding up the core, and start using some other
> system for them.
>
BTW, isn't certik/remove_assumptions slightly more recent? Anyway, you
never explained why the old assumptions are slowing us down. Everything
I've seen so far points in the other direction.

> I would be interested in the community vote on this idea. I vote +1. I
> am aware that Ronan voted -1 last year. What do others think?

What are you voting +1 on, concretely? Your branch is one year old, so
it would have to be updated. There were also many test failures
remaining and it was unbearably slow for complex calculations, what's
your plan for that?

Vinzent Steinberg

unread,
May 1, 2011, 11:18:15 AM5/1/11
to sympy
On May 1, 8:47 am, Ondrej Certik <ond...@certik.cz> wrote:
> I am still very much convinced, that this disconnect-assumptions-2 is
> the simplest and easiest way to get rid of the old assumptions, so
> that we can start speeding up the core, and start using some other
> system for them.

I'm not convinced, because it uses global assumptions, and I think
they are broken by design. Global assumptions basically let us do the
garbage collection manually, a regression compared to the old system.

> I would be interested in the community vote on this idea. I vote +1. I
> am aware that Ronan voted -1 last year. What do others think?

I don't think that any branch is ready to be merged now, so maybe
let's rather discuss about the approach for future development. For
instance, what do you think about the local assumptions approach (see
the wiki page and issue 1884)? It's a hack, but I think it is much
better than using global assumptions.

Vinzent

Haz

unread,
May 1, 2011, 11:37:06 AM5/1/11
to sy...@googlegroups.com
>   There was push-back from the community since removing the
> Symbol('x', positive=True) syntax was largely frowned upon.

As I recall, the problems were that it was slow and broke a lot of
things. Symbol(x, positive=True) is easy to hack back in.

  The speed degradation was primarily due to the cache being disabled (which the second branch avoided by flushing the cache after every assumption use).

>   I don't think this is the case. Trivial queries (those that already
> have obvious implications) are compiled into quick reference
> dictionaries. In particular:
> - https://github.com/sympy/sympy/blob/master/sympy/assumptions/ask.py#L114
>
I think that the problem is with non-trivial queries, for which that
code does nothing.

  The non-trivial queries are broken up, and non-trivial facts are compiled and stored (the lookup isn't just on the facts we input, but on the facts that can be derived). Having a good example of a query that the old assumption system can perform (and the new one can't) would be good to have.
 
The cache is the source of a lot of problems. We should try to use it as
little as possible, even if it's not really possible to remove it at
this time.

  So what are your thoughts on flushing it at the end of every computation?

The idea is to write '~Q.real(x)' instead of 'Assume(x, Q.real, False)'.
It's shorter, easier to understand and doesn't suggest wrongly that
creating the object does anything beyond that.

  Only issue with this is that we'll need to start handling (in the assumption context), arbitrary boolean formulae: Q.positive(x) | Q.negative(x)

Haz

unread,
May 1, 2011, 11:40:00 AM5/1/11
to sy...@googlegroups.com
  The cache issue runs deeper -- variable x, that depends on variably y, may simplify to something that doesn't even involve y because of the assumptions that come with y. In this case, a result is stored for x that is only valid under the right assumptions for y, and we would need to be very careful about what cache entries change when we delete y.

  The example may sound contrived, but it does arise in practice, and was a headache to debug.


--

Haz

unread,
May 1, 2011, 11:45:31 AM5/1/11
to sy...@googlegroups.com
> I would be interested in the community vote on this idea. I vote +1. I
> am aware that Ronan voted -1 last year. What do others think?

What are you voting +1 on, concretely? Your branch is one year old, so
it would have to be updated. There were also many test failures
remaining and it was unbearably slow for complex calculations, what's
your plan for that?

  I'll have to agree that things have changed too significantly to cherry pick anything from the past, but it's the method of doing things we need to agree on.

Ondrej: I think the assumptions-2 branch assumed everything would go into the global assumptions context (and be flushed along with the cache after use), and Symbol('x', positive=True) was removed. Is this the method you're voting for?

Ronan: "unbearably slow for complex calculations" --> can you throw an example up on the wiki page so we can look into addressing this?

Ronan Lamy

unread,
May 1, 2011, 11:55:40 AM5/1/11
to sy...@googlegroups.com
Le dimanche 01 mai 2011 à 11:37 -0400, Haz a écrit :

> The idea is to write '~Q.real(x)' instead of 'Assume(x,
> Q.real, False)'.
> It's shorter, easier to understand and doesn't suggest wrongly
> that
> creating the object does anything beyond that.
>
>
> Only issue with this is that we'll need to start handling (in the
> assumption context), arbitrary boolean formulae: Q.positive(x) |
> Q.negative(x)
>

Yes, but that's not hard to do. And in master, we already have:

In [1]: ask(x, Q.integer, Q.even(x) | Q.odd(x))
Out[1]: True

Ondrej Certik

unread,
May 1, 2011, 12:36:34 PM5/1/11
to sy...@googlegroups.com
On Sun, May 1, 2011 at 8:18 AM, Vinzent Steinberg
<vinzent....@googlemail.com> wrote:
> On May 1, 8:47 am, Ondrej Certik <ond...@certik.cz> wrote:
>> I am still very much convinced, that this disconnect-assumptions-2 is
>> the simplest and easiest way to get rid of the old assumptions, so
>> that we can start speeding up the core, and start using some other
>> system for them.
>
> I'm not convinced, because it uses global assumptions, and I think
> they are broken by design. Global assumptions basically let us do the
> garbage collection manually, a regression compared to the old system.

There is some misunderstanding (possibly on my side): I thought that
the branch has removed the assumptions completely, thus it uses no
assumptions. Then the idea was to use *local* assumptions, by using
the Assume class.

Can you clarify what you mean by "global assumptions" in this branch?
I am aware that the code in sympy/assumptions also (besides other
things) allows to use global assumptions, and I stress that this is
not the approach I am voting for ---- I am voting for using a local
Assume (or AssumeContext) whatever you want to call it.

>
>> I would be interested in the community vote on this idea. I vote +1. I
>> am aware that Ronan voted -1 last year. What do others think?
>
> I don't think that any branch is ready to be merged now, so maybe
> let's rather discuss about the approach for future development. For

Yes, I am voting for that approach in the branch. Of course it has to
be rebased on top of the latest master (or redo from scratch, whatever
is easier).

> instance, what do you think about the local assumptions approach (see
> the wiki page and issue 1884)? It's a hack, but I think it is much
> better than using global assumptions.

I looked at the code, most importantly here:

https://github.com/vks/sympy/commit/bfc837b8859992c7ce43bd4967902bfcec756252

and even though the hack is simple, it is still a hack, so my gut
feeling is telling me, that this will bite is quite substantially in
the future. (E.g. if you want to rewrite the core in C/C++, will this
design still work?)

However, apart from this hack, your approach seems identical to what I
have in mind --- except that I would pass the AssumptionContext (that
you embed automatically in the local scope) around by hand explicitly
("explicit is better than implicit").

Ondrej

Ondrej Certik

unread,
May 1, 2011, 12:43:59 PM5/1/11
to sy...@googlegroups.com
On Sun, May 1, 2011 at 7:43 AM, Ronan Lamy <ronan...@gmail.com> wrote:
> Le samedi 30 avril 2011 à 23:47 -0700, Ondrej Certik a écrit :
>> On Sat, Apr 30, 2011 at 10:36 AM, Haz <christi...@gmail.com> wrote:
>> > So a few things to respond to...
>> > Tom:
>> >>Can you describe what went wrong with these branches? In particular,
>> >>why can the following naive strategy not work:
>> >>
>> >>1. Make the new system at least as good as the old one. That is
>> >>whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I will
>> >>get at least as good an answer. In particular Symbol('x',
>> >>positive=True) should register automatically in the global assumptions
>> >>that x > 0 (if I understand correctly how the new system works...).
>> >>2. Replace all queries foo.is_bar by ask(foo, Q.bar).
>> >>3. Remove all implementations of the is_bar properties.
>> >>4. Remove all remaining remnants of the old system.
>> >   This is what was largely tried with a branch last summer:
>> > - https://github.com/haz/sympy/tree/disconnect-assumptions-2
>> >   There was push-back from the community since removing the Symbol('x',
>> > positive=True) syntax was largely frowned upon.
>>
>> I am still very much convinced, that this disconnect-assumptions-2 is
>> the simplest and easiest way to get rid of the old assumptions, so
>> that we can start speeding up the core, and start using some other
>> system for them.
>>
> BTW, isn't certik/remove_assumptions slightly more recent? Anyway, you

I am not sure at the moment, but the branch I am referring to is the
one that Christian worked for about a week during his GSoC. Then the
discouragement from the community forced him to stop.

> never explained why the old assumptions are slowing us down. Everything
> I've seen so far points in the other direction.

I claim that no assumptions are faster than assumptions. I claim that
the core should not deal with assumptions by default, just like ginac
(that Sage uses) doesn't deal with assumptions, and thus is fast.
Again, this is necessary in order to achieve the goal of creating a
usable CAS (comparable to Mathematica).

>
>> I would be interested in the community vote on this idea. I vote +1. I
>> am aware that Ronan voted -1 last year. What do others think?
>
> What are you voting +1 on, concretely? Your branch is one year old, so

I vote to continue on that branch, which passed almost all tests
(rebase it/rework it to the latest master).

> it would have to be updated. There were also many test failures

Those need to be fixed, just like the ones in the core, that were already fixed.

> remaining and it was unbearably slow for complex calculations, what's
> your plan for that?

As Christian remarked, it was slower if the caching was turned down.
Caching is another problem, that needs to be addressed, as Christian
remarked. My plan is that preferably the cache should be off by
default, and/or flushing it the way Christian did it. If I remember
well, it was not slower.

Ondrej

Chris Smith

unread,
May 1, 2011, 1:11:36 PM5/1/11
to sy...@googlegroups.com
I've made modifications in https://github.com/sympy/sympy/pull/267

When I run the matrices.txt file alone all tests pass. When I run the full
doctest suite I get the failure below for QR decomposition. When I shut
caching off and run the suite, it passes. How can caching give a wrong
answer? It's not just different, it's wrong as shown below.

**********************************************************************
File "c:\documents and settings\chris\sympy\doc\src\modules\matrices.txt", line
374, in matrices.txt
Failed example:
Q
Expected:
[ ___ ___ ___]
[\/ 6 -\/ 3 -\/ 2 ]
[----- ------ ------]
[ 6 3 2 ]
[ ]
[ ___ ___ ___ ]
[\/ 6 -\/ 3 \/ 2 ]
[----- ------ ----- ]
[ 6 3 2 ]
[ ]
[ ___ ___ ]
[\/ 6 \/ 3 ]
[----- ----- 0 ]
[ 3 3 ]
Got:
[ ___ ___]
[\/ 6 -11*\/ 2 ]
[----- 2/3 ---------]
[ 6 18 ]
[ ]
[ ___ ___ ]
[\/ 6 -5*\/ 2 ]
[----- 2/3 -------- ]
[ 6 18 ]
[ ]
[ ___ ___ ]
[\/ 6 -2*\/ 2 ]
[----- 1/3 -------- ]
[ 3 9 ]
**********************************************************************
File "c:\documents and settings\chris\sympy\doc\src\modules\matrices.txt", line
389, in matrices.txt
Failed example:
R
Expected:
[ ___ ]
[ ___ 4*\/ 6 ___]
[\/ 6 ------- 2*\/ 6 ]
[ 3 ]
[ ]
[ ___ ]
[ \/ 3 ]
[ 0 ----- 0 ]
[ 3 ]
[ ]
[ ___ ]
[ 0 0 \/ 2 ]
Got:
[ ___ ]
[ ___ 4*\/ 6 ___]
[\/ 6 ------- 2*\/ 6 ]
[ 3 ]
[ ]
[ 0 1 4 ]
[ ]
[ ___]
[ 0 0 3*\/ 2 ]
**********************************************************************
1 items had failures:
2 of 97 in matrices.txt
***Test Failed*** 2 failures.
doc\src\modules\matrices.txt [97] [2]

This wouldn't be so bad, but Q*R doesn' even equal the original matrix, giving

h[2] >>> pprint(Q)
[ ___ ___]
[\/ 6 -11*\/ 2 ]
[----- 2/3 ---------]
[ 6 18 ]
[ ]
[ ___ ___ ]
[\/ 6 -5*\/ 2 ]
[----- 2/3 -------- ]
[ 6 18 ]
[ ]
[ ___ ___ ]
[\/ 6 -2*\/ 2 ]
[----- 1/3 -------- ]
[ 3 9 ]
h[2] >>> pprint(R)
[ ___ ]
[ ___ 4*\/ 6 ___]
[\/ 6 ------- 2*\/ 6 ]
[ 3 ]
[ ]
[ 0 1 4 ]
[ ]
[ ___]
[ 0 0 3*\/ 2 ]
h[2] >>> Q*R
[1, 2, 1]
[1, 2, 3]
[2, 3, 4]

Q*R should be
[1, 1, 1]
[1, 1, 3]
[2, 3, 4]

Does anyone have any idea what the problem might be? I don't even
know how to go about debuging this?

/c

Aaron S. Meurer

unread,
May 1, 2011, 1:48:02 PM5/1/11
to sy...@googlegroups.com
Hi.

Can everyone add these ideas to that wiki page? So there should be global assumptions, local assumptions, etc., with links to the relevant branches, and explanations of the idea, the pros, the cons, etc. I think it will be a lot easier to just read through the wiki page whenever we make a decision, rather than trying to read through this and other discussions.

If I remember correctly, it would be easier to implement in C, because there you have pointers. Also, you wouldn't need a hack in Python 3, because there you have nonlocal (or is this something else completely?)

See also issue 1884. Also, would the idea from comment 3 of that issue (wrapping something around a mutable object) work to make it less hackish?

Aaron Meurer

>
> However, apart from this hack, your approach seems identical to what I
> have in mind --- except that I would pass the AssumptionContext (that
> you embed automatically in the local scope) around by hand explicitly
> ("explicit is better than implicit").
>

> Ondřej

Tom Bachmann

unread,
May 1, 2011, 3:30:27 PM5/1/11
to sympy


On 30 Apr., 23:41, "Aaron S. Meurer" <asmeu...@gmail.com> wrote:
> On Apr 30, 2011, at 1:24 PM, Ronan Lamy wrote:
>
>
>
> > Le samedi 30 avril 2011 à 13:36 -0400, Haz a écrit :
> >> So a few things to respond to...
>
> >> Tom:
> >>> Can you describe what went wrong with these branches? In particular,
> >>> why can the following naive strategy not work:
>
> >>> 1. Make the new system at least as good as the old one. That is
> >>> whenever I can write foo.is_bar I can write ask(foo, Q.bar) and I
> >> will
> >>> get at least as good an answer. In particular Symbol('x',
> >>> positive=True) should register automatically in the global
> >> assumptions
> >>> that x > 0 (if I understand correctly how the new system works...).
> >>> 2. Replace all queries foo.is_bar by ask(foo, Q.bar).
> >>> 3. Remove all implementations of the is_bar properties.
> >>> 4. Remove all remaining remnants of the old system.
>
> >>  This is what was largely tried with a branch last summer:
> >> -https://github.com/haz/sympy/tree/disconnect-assumptions-2
>
> >>  There was push-back from the community since removing the
> >> Symbol('x', positive=True) syntax was largely frowned upon.
>
> > As I recall, the problems were that it was slow and broke a lot of
> > things. Symbol(x, positive=True) is easy to hack back in.
>
> >> Vinzent:
> >>> I think the only problem could be that ask() is much slower for
> >>> trivial queries, needs to be verified. This should be not too hard to
> >>> fix though.
>
> >>  I don't think this is the case. Trivial queries (those that already
> >> have obvious implications) are compiled into quick reference
> >> dictionaries. In particular:
> >> -https://github.com/sympy/sympy/blob/master/sympy/assumptions/ask.py#L114
Why? For example in gruntz, the performance depends quite a lot of
caching mrv computations, limit computations, and order computations.
Since these hardly arise in a controlled manner, the only way I can
see to not recompute is to roll a separate cache - what would be the
point of this?

IMHO we should definitely keep the cache. When something breaks it, it
is ill-designed.

Tom Bachmann

unread,
May 1, 2011, 3:35:47 PM5/1/11
to sympy
On 1 Mai, 16:40, Haz <christian.mu...@gmail.com> wrote:
>   The cache issue runs deeper -- variable x, that depends on variably y, may
> simplify to something that doesn't even involve y because of the assumptions
> that come with y. In this case, a result is stored for x that is only valid
> under the right assumptions for y, and we would need to be very careful
> about what cache entries change when we delete y.
>
>   The example may sound contrived, but it does arise in practice, and was a
> headache to debug.
>

Well there must be an end to it, right? Every assumption is about
symbols. Every symbol x that depends in any way on another symbol y
has y in its free_symbols. In fact any exrpession has all symbols it
depends on (recursively) in its free_symbols. Hence it would be enough
to pull all assumptions involving any free symbols into the hash
computation.

Right? [If not then one point in the above chain of argument must be
wrong; this is very well possible :-)]

Of course then we want a quick way to find all assumptions involving a
given symbol. The easiest would be to have the symbol store a list of
assumptions it is involved in. … Which brings us back to ronan's
comment that all this is not much different from storing assumptions
with the symbols anyway.

Tom Bachmann

unread,
May 1, 2011, 3:36:48 PM5/1/11
to sympy
In fact in master Assume(x, Q.positive, False) gets turned into
Not(...) already (which upsets assumptions context btw).

Aaron S. Meurer

unread,
May 1, 2011, 3:38:05 PM5/1/11
to sy...@googlegroups.com

On May 1, 2011, at 1:35 PM, Tom Bachmann wrote:

> On 1 Mai, 16:40, Haz <christian.mu...@gmail.com> wrote:
>> The cache issue runs deeper -- variable x, that depends on variably y, may
>> simplify to something that doesn't even involve y because of the assumptions
>> that come with y. In this case, a result is stored for x that is only valid
>> under the right assumptions for y, and we would need to be very careful
>> about what cache entries change when we delete y.
>>
>> The example may sound contrived, but it does arise in practice, and was a
>> headache to debug.
>>
>
> Well there must be an end to it, right? Every assumption is about
> symbols. Every symbol x that depends in any way on another symbol y
> has y in its free_symbols. In fact any exrpession has all symbols it
> depends on (recursively) in its free_symbols. Hence it would be enough
> to pull all assumptions involving any free symbols into the hash
> computation.
>
> Right? [If not then one point in the above chain of argument must be
> wrong; this is very well possible :-)]
>
> Of course then we want a quick way to find all assumptions involving a
> given symbol. The easiest would be to have the symbol store a list of
> assumptions it is involved in. … Which brings us back to ronan's
> comment that all this is not much different from storing assumptions
> with the symbols anyway.

Even if we were to decide that this was a good idea (I'm personally still not convinced), we also need to consider the speed implications. Hash computation should be very fast.

Aaron Meurer

Haz

unread,
May 1, 2011, 3:45:18 PM5/1/11
to sy...@googlegroups.com
>   The example may sound contrived, but it does arise in practice, and was a
> headache to debug.
>

Well there must be an end to it, right? Every assumption is about
symbols. Every symbol x that depends in any way on another symbol y
has y in its free_symbols. In fact any exrpession has all symbols it
depends on (recursively) in its free_symbols. Hence it would be enough
to pull all assumptions involving any free symbols into the hash
computation.

Right? [If not then one point in the above chain of argument must be
wrong; this is very well possible :-)]

  x may not depend on y if the right assumptions exist on y, and x is simplified. What you're suggesting may work if the hash is computed before the simplification occurs, but then you're bringing information about the equation into the hash before the equation is dealt with (and as Aaron points out, this could have some performance issues).

  But back to my original suggestion -- can anyone see something wrong with flushing the cache after a sympy command is invoked? The reason this has potential for solving the problem is because the assumptions aren't apt to changing while sympy is computing a result (only in between commands). This means the gruntz can still do its thing with the cache, etc. 

Tom Bachmann

unread,
May 1, 2011, 3:52:54 PM5/1/11
to sympy
On 1 Mai, 20:45, Haz <christian.mu...@gmail.com> wrote:
> > >   The example may sound contrived, but it does arise in practice, and was
> > a
> > > headache to debug.
>
> > Well there must be an end to it, right? Every assumption is about
> > symbols. Every symbol x that depends in any way on another symbol y
> > has y in its free_symbols. In fact any exrpession has all symbols it
> > depends on (recursively) in its free_symbols. Hence it would be enough
> > to pull all assumptions involving any free symbols into the hash
> > computation.
>
> > Right? [If not then one point in the above chain of argument must be
> > wrong; this is very well possible :-)]
>
>   x may not depend on y if the right assumptions exist on y, and x is
> simplified. What you're suggesting may work if the hash is computed before
> the simplification occurs, but then you're bringing information about the
> equation into the hash before the equation is dealt with (and as Aaron
> points out, this could have some performance issues).
>

I'm not sure I understand. The result of any given invocation of a
function should depend only on the arguments passed, and possibly on
assumptions on the arguments. If the assumptions are stored with the
arguments we are fine, if the assumptions are passed around
explicitely (as ondej suggests) we are fine, if the assumptions are
stored separately, they have to be pulled in.

>   But back to my original suggestion -- can anyone see something wrong with
> flushing the cache after a sympy command is invoked? The reason this has
> potential for solving the problem is because the assumptions aren't apt to
> changing while sympy is computing a result (only in between commands). This
> means the gruntz can still do its thing with the cache, etc.

This still feels wrong to me. Firstly, how do you even define "after a
sympy command"? Sympy is a library with many entry opints (at least I
see it as such).
Secondly, even inside sympy assumptions can be changed. For example
the gruntz algorithm always (locally) adds an assumption to the
variable with respect to which the limit is being taken (it currently
uses a fresh Dummy, but with flexible assumptions in place this would
not be necessary).

Ondrej Certik

unread,
May 1, 2011, 4:13:23 PM5/1/11
to sy...@googlegroups.com

Because the cache is a source of tons of our problems with sympy. Very
hard to debug problems. Designing an efficient and robust cache is a
highly nontrivial task.

So if gruntz needs it, then it should use it's own private cache,
until we figure out a better algorithm, that doesn't need it. The core
of sympy should not use any cache however.

Ondrej

Haz

unread,
May 1, 2011, 4:40:19 PM5/1/11
to sy...@googlegroups.com
This is the chunk that things get done in:

  Have a global variable called working, and a local variable called first. Put this at the top of wrapper:

first = False

if not working:
    working = True
    first = True

...and just before returning anything:

if first:
    working = False
    clear_cache() # Or whatever equivalent is needed to flush it out.

  Essentially you only enable the cache for the time that sympy is computing something. And yes, gruntz uses some assumptions during it's calculations, but that is a rare case and can be handled with local assumption contexts or cleaning the assumptions they use if it is global (which is how I handled it last summer).

  The only overhead this should impose is cleaning the cache at the end of a computation -- minimal I would suspect.


--

Aaron S. Meurer

unread,
May 1, 2011, 5:35:16 PM5/1/11
to sy...@googlegroups.com
On May 1, 2011, at 2:40 PM, Haz wrote:

This is the chunk that things get done in:

  Have a global variable called working, and a local variable called first. Put this at the top of wrapper:

first = False

if not working:
    working = True
    first = True

...and just before returning anything:

if first:
    working = False
    clear_cache() # Or whatever equivalent is needed to flush it out.

If I understand it correctly, this assumes that the cache is needed only within calls of the outermost decorated function, but not between them.  Does this hold for the Gruntz code that uses the caching?

Aaron Meurer

Vinzent Steinberg

unread,
May 1, 2011, 5:49:57 PM5/1/11
to sympy
On 1 Mai, 11:37, Haz <christian.mu...@gmail.com> wrote:
>   The non-trivial queries are broken up, and non-trivial facts are compiled
> and stored (the lookup isn't just on the facts we input, but on the facts
> that can be derived). Having a good example of a query that the old
> assumption system can perform (and the new one can't) would be good to have.

I think we don't need to worry about non-trivial queries (and their
performance) for now, because the old systems AFAIK only supports
trivial queries.

Vinzent

Haz

unread,
May 1, 2011, 6:04:09 PM5/1/11
to sy...@googlegroups.com
The correctness of any algorithm should be unaffected by the cache --
so you would only see a (time) difference if repeated sympy commands
use the cached results.

Vinzent Steinberg

unread,
May 1, 2011, 6:16:21 PM5/1/11
to sympy
On 1 Mai, 18:36, Ondrej Certik <ond...@certik.cz> wrote:
> There is some misunderstanding (possibly on my side): I thought that
> the branch has removed the assumptions completely, thus it uses no
> assumptions. Then the idea was to use *local* assumptions, by using
> the Assume class.
>
> Can you clarify what you mean by "global assumptions" in this branch?

You have tests like:

def test_PDF():
a = Symbol('a')
x = Symbol('x')

global_assumptions.add(Assume(a, Q.positive, True))
global_assumptions.add(Assume(x, Q.real, True))

exponential = PDF(exp(-x/a), (x,0,oo))
exponential = exponential.normalize()
assert exponential.pdf(x) == 1/a*exp(-x/a)
assert exponential.cdf(x) == 1 - exp(-x/a)
assert exponential.mean == a
assert exponential.variance == a**2
assert exponential.stddev == a

global_assumptions.discard(Assume(a, Q.positive, True))
global_assumptions.discard(Assume(x, Q.real, True))


To me it does not make sense to have one big set that stores
absolutely all assumptions.

> I am aware that the code in sympy/assumptions also (besides other
> things) allows to use global assumptions, and I stress that this is
> not the approach I am voting for ---- I am voting for using a local
> Assume (or AssumeContext) whatever you want to call it.

This adds a lot of verbosity compared to the old way.

> >> I would be interested in the community vote on this idea. I vote +1. I
> >> am aware that Ronan voted -1 last year. What do others think?
>
> > I don't think that any branch is ready to be merged now, so maybe
> > let's rather discuss about the approach for future development. For
>
> Yes, I am voting for that approach in the branch. Of course it has to
> be rebased on top of the latest master (or redo from scratch, whatever
> is easier).
>
> > instance, what do you think about the local assumptions approach (see
> > the wiki page and issue 1884)? It's a hack, but I think it is much
> > better than using global assumptions.
>
> I looked at the code, most importantly here:
>
> https://github.com/vks/sympy/commit/bfc837b8859992c7ce43bd4967902bfce...
>
> and even though the hack is simple, it is still a hack, so my gut
> feeling is telling me, that this will bite is quite substantially in
> the future. (E.g. if you want to rewrite the core in C/C++, will this
> design still work?)

This is a good point, I felt free to add it to the wiki. We don't have
to use local assumptions. The idea is that it allows a more painless
transition. We can still replace every use of it with a more explicit
context passing. We could also implement a special data structure that
stores the local contexts, emulating the same behavior, so that it can
be implemented in C. This is however much more work than this simple
hack, where Python does the hard stuff for us.

> However, apart from this hack, your approach seems identical to what I
> have in mind --- except that I would pass the AssumptionContext (that
> you embed automatically in the local scope) around by hand explicitly
> ("explicit is better than implicit").

I think even if it is a hack, it is a well-defined one, with clear
behavior. How do you imagine the example above with explicit
assumption passing? Something like

def test_PDF():
a = Symbol('a')
x = Symbol('x')

ctx = AssumptionContext()
ctx.add(Assume(a, Q.positive, True))
ctx.add(Assume(x, Q.real, True))

exponential = PDF(exp(-x/a), (x,0,oo), ctx)
exponential = exponential.normalize()
assert exponential.pdf(x) == 1/a*exp(-x/a)
assert exponential.cdf(x) == 1 - exp(-x/a)
assert exponential.mean == a
assert exponential.variance == a**2
assert exponential.stddev == a

?

This would need a lot or reimplementing.

Vinzent

Ronan Lamy

unread,
May 1, 2011, 10:10:47 PM5/1/11
to sy...@googlegroups.com

Sure, it's faster if you don't have assumptions than if you need to deal
with them. But we need them: how else could we simplify, for instance,
sqrt(x**2)?

I believe you think that we shouldn't simplify anything implicitly, but
in that case expressions get unwieldy very fast - you can't even assume
that Symbols are commutative. So you have to check for simplifications
all the time, which brings you back to dealing with assumptions all the
time, except you've traded a system that was optimised for that kind of
checking for one that isn't.



> >
> >> I would be interested in the community vote on this idea. I vote +1. I
> >> am aware that Ronan voted -1 last year. What do others think?
> >
> > What are you voting +1 on, concretely? Your branch is one year old, so
>
> I vote to continue on that branch, which passed almost all tests
> (rebase it/rework it to the latest master).
>
> > it would have to be updated. There were also many test failures
>
> Those need to be fixed, just like the ones in the core, that were already fixed.
>
> > remaining and it was unbearably slow for complex calculations, what's
> > your plan for that?
>
> As Christian remarked, it was slower if the caching was turned down.
> Caching is another problem, that needs to be addressed, as Christian
> remarked. My plan is that preferably the cache should be off by
> default, and/or flushing it the way Christian did it. If I remember
> well, it was not slower.

Well, turning off the cache slows sympy so much that it's not a
realistic option at the moment.

Ondrej Certik

unread,
May 2, 2011, 2:37:59 AM5/2/11
to sy...@googlegroups.com

Refine(sqrt(x**2), ctx)

where you pass some Assumption context, or assumptions.

>
> I believe you think that we shouldn't simplify anything implicitly, but
> in that case expressions get unwieldy very fast - you can't even assume
> that Symbols are commutative. So you have to check for simplifications

You should assume it. Non commutative symbols are just making things
very complicated, and it was a mistake to add them into the core of
sympy. They should start in sympy.quantum, and only after they are
being used a lot, we should start thinking how to handle them
properly.

> all the time, which brings you back to dealing with assumptions all the
> time, except you've traded a system that was optimised for that kind of
> checking for one that isn't.

We disagree here. I think that you should *not* deal with assumptions
in the core.

>
>> >
>> >> I would be interested in the community vote on this idea. I vote +1. I
>> >> am aware that Ronan voted -1 last year. What do others think?
>> >
>> > What are you voting +1 on, concretely? Your branch is one year old, so
>>
>> I vote to continue on that branch, which passed almost all tests
>> (rebase it/rework it to the latest master).
>>
>> > it would have to be updated. There were also many test failures
>>
>> Those need to be fixed, just like the ones in the core, that were already fixed.
>>
>> > remaining and it was unbearably slow for complex calculations, what's
>> > your plan for that?
>>
>> As Christian remarked, it was slower if the caching was turned down.
>> Caching is another problem, that needs to be addressed, as Christian
>> remarked. My plan is that preferably the cache should be off by
>> default, and/or flushing it the way Christian did it. If I remember
>> well, it was not slower.
>
> Well, turning off the cache slows sympy so much that it's not a
> realistic option at the moment.

I disagree, as explained in the previous paragraph --- I don't think
it was slower to use Christian's approach. Also, I know that the core
can be very fast, as shown by the csympy, *without* caching.

As such, when you can be both fast (faster than now) and not use
cache, you should go for it.

So my own vision has always been very clear:

* get rid of assumptions in the core
* use csympy (or use ideas from it) to speed up the core
* use caching in other parts of sympy, that need it, e.g. limits,
maybe integrals, I don't know
* implement refine(), and other things, and use the new assumptions.

Ondrej

Tom Bachmann

unread,
May 2, 2011, 3:30:02 AM5/2/11
to sympy
Alright, so I took Vinzent's old branch and made the most important
things work over current master - see my local_assump branch [*]. I
also added the hashing extensions for caching I described. Now only
someone has to be daring enough to rewrite all the .is_foo properties
and see how much breaks.

[*] There are a few test failures, but nothing unfixable.

@Haz: Can you take a look if this implementation of caching is still
amenable to the problems you described? There is a test in
test_cache.py that you could take as a baseline.

Even if we decide that assumptions should all be passed around
explicitely, I think assumption injection is a good transition
strategy.

Fredrik Johansson

unread,
May 2, 2011, 3:50:22 AM5/2/11
to sy...@googlegroups.com
Most of the problems with assumptions in SymPy are symptoms of larger
design problems which also affect performance, code clarity, and
customizability. The (semi-)new polynomial code, which supports
explicit coefficient rings (and term orders, etc), goes a long way to
address such issues, although its scope is limited.

For general symbolics, the analog would be to allow constructing
symbolic algebras, of which particular expressions would be elements
(compare with Parent/Element in Sage). Then simplification routines
would just be methods of the algebra class, and assumptions would be
properties attached to the algebra. For example, one could disable all
(or some) simplifications through subclassing. I'm almost convinced
that this is the cleanest object-oriented way to do it, since the
algebra would encapsulate all state and more.

Everything else is then just a matter of syntax (e.g. one can have
"global" assumptions by using a mutable global default algebra, and
one can have "local" assumptions by creating a local/temporary algebra
instance for this purpose). Caches could also be properties of
algebras. This would also allow algorithms to construct new algebras
for internal use, use any assumptions, caching, etc., and be sure
that this wouldn't have an effect on the outside world.

I did a similar thing with the "contexts" in mpmath, although I didn't
really go all the way (creating multiple instances of the same context
class is still a bit flaky, and doesn't work in Sage, but the fp and
iv contexts show how powerful this approach is). This helped
*tremendously* with writing the Cython backend in Sage, anyhow.

Fredrik

Haz

unread,
May 2, 2011, 11:46:26 AM5/2/11
to sy...@googlegroups.com
Tom: Apologies, but I'm having trouble inferring what you github username is -- where is the branch?

Fredrik: I like the idea, but that seems like a massive shift in the SymPy core that I don't feel is possible in the time frame that the assumptions need to be fixed in.

Tom Bachmann

unread,
May 2, 2011, 12:17:10 PM5/2/11
to sympy


On 2 Mai, 16:46, Haz <christian.mu...@gmail.com> wrote:
> Tom: Apologies, but I'm having trouble inferring what you github username is
> -- where is the branch?
>

Oh I'm sorry … it's ness01. Branch is at https://github.com/ness01/sympy/tree/local_assump

> Fredrik: I like the idea, but that seems like a massive shift in the SymPy
> core that I don't feel is possible in the time frame that the assumptions
> need to be fixed in.
>

Ronan Lamy

unread,
May 2, 2011, 12:25:24 PM5/2/11
to sy...@googlegroups.com
Obviously, this deal with assumptions, but it's explicit. Which is
exactly the case I discussed in the followingparagraph.

> >
> > I believe you think that we shouldn't simplify anything implicitly, but
> > in that case expressions get unwieldy very fast - you can't even assume
> > that Symbols are commutative. So you have to check for simplifications
>
> You should assume it. Non commutative symbols are just making things
> very complicated, and it was a mistake to add them into the core of
> sympy. They should start in sympy.quantum, and only after they are
> being used a lot, we should start thinking how to handle them
> properly.

I agree on this. Non-commutative Symbols don't make a lot of sense. If
we didn't have them (and severed the inheritance from Boolean), we could
say that Symbols are complex variables. Instead, we have a murky
definition.
Anyway, is_commutative is just an extreme example. Most .__new__()
and .eval() methods check one assumption or other. We'd have to throw
all that code away and rewrite it from scratch.

> > all the time, which brings you back to dealing with assumptions all the
> > time, except you've traded a system that was optimised for that kind of
> > checking for one that isn't.
>
> We disagree here. I think that you should *not* deal with assumptions
> in the core.

And I'm saying that no matter what you think we should do, we can't
avoid dealing with them, unless, perhaps, we do a complete rewrite as
Fredrik suggests.

> >> >> I would be interested in the community vote on this idea. I vote +1. I
> >> >> am aware that Ronan voted -1 last year. What do others think?
> >> >
> >> > What are you voting +1 on, concretely? Your branch is one year old, so
> >>
> >> I vote to continue on that branch, which passed almost all tests
> >> (rebase it/rework it to the latest master).
> >>
> >> > it would have to be updated. There were also many test failures
> >>
> >> Those need to be fixed, just like the ones in the core, that were already fixed.
> >>
> >> > remaining and it was unbearably slow for complex calculations, what's
> >> > your plan for that?
> >>
> >> As Christian remarked, it was slower if the caching was turned down.
> >> Caching is another problem, that needs to be addressed, as Christian
> >> remarked. My plan is that preferably the cache should be off by
> >> default, and/or flushing it the way Christian did it. If I remember
> >> well, it was not slower.
> >
> > Well, turning off the cache slows sympy so much that it's not a
> > realistic option at the moment.
>
> I disagree, as explained in the previous paragraph --- I don't think
> it was slower to use Christian's approach. Also, I know that the core

Sympy is most certainly much slower in your branch than in master with
cache on. Just try "bin/test sympy/solvers" in both.

> can be very fast, as shown by the csympy, *without* caching.
>

What's csympy? And can it do the same things as sympy? Otherwise, you're
comparing apples to oranges.

> As such, when you can be both fast (faster than now) and not use
> cache, you should go for it.
>
> So my own vision has always been very clear:
>
> * get rid of assumptions in the core
> * use csympy (or use ideas from it) to speed up the core
> * use caching in other parts of sympy, that need it, e.g. limits,
> maybe integrals, I don't know
> * implement refine(), and other things, and use the new assumptions.

The problem I see with that plan is that you break sympy at step 1, and
fix it at step 4.

Fredrik Johansson

unread,
May 2, 2011, 1:26:29 PM5/2/11
to sy...@googlegroups.com
On Mon, May 2, 2011 at 5:46 PM, Haz <christi...@gmail.com> wrote:
> Tom: Apologies, but I'm having trouble inferring what you github username is
> -- where is the branch?
> Fredrik: I like the idea, but that seems like a massive shift in the SymPy
> core that I don't feel is possible in the time frame that the assumptions
> need to be fixed in.

I don't think it's necessarily that much work. The only essential
thing that needs to be done is to provide every expression object with
a reference to its parent algebra, and to overwrite sympification to
convert inputs to have the same parent. Everything else can be
implemented gradually. The major obstacles ought to be caching and the
current assumptions -- which need to go anyway.

Fredrik

Tom Bachmann

unread,
May 2, 2011, 1:42:29 PM5/2/11
to sympy
On 2 Mai, 18:26, Fredrik Johansson <fredrik.johans...@gmail.com>
wrote:
Would it be too much to ask for a coherent outline of this proposal on
the wiki? (By coherent I don't mean "less confused" but "more
complete".) What you describe indeed sounds nice and clean to me, but
I have difficulty piecing things together (in particular I know e.g.
nothing about sage).

Tom

Haz

unread,
May 2, 2011, 1:49:53 PM5/2/11
to sy...@googlegroups.com
  A summary would indeed help. I have another fear though, that this idea (while enticing) is not largely understood (afaik). This puts the burden of re-working SymPy into mainly your hands, Fredrik, where patching SymPy to work with the new assumptions can be jointly tackled (evidenced by the vast number of branches that exist for doing just that).

Haz

unread,
May 2, 2011, 2:35:35 PM5/2/11
to sy...@googlegroups.com
  I feel that incorporating the assumptions into the hash key is unnecessary and bogs things down. I haven't had the time to fully pick through that branch, but running the tests doubles the time from ~330sec to ~760sec. If you simply flush in between calculations, it only adds ~20sec to the mix:

  That commit (from what I can recall of last year's debugging sessions) would be enough to fix 99.9% of the problems with the cache -vs- assumptions. The other 0.1% involves treating the (new) assumptions used in the core with the utmost care -- I'm of the opinion that they should be used (i.e., new assumptions created) very little (possibly not at all) in the core of SymPy.

  The disabling of the cache was definitely a bog from last year, but I was able to bring the speeds back up to acceptable limits by using the cache and aggressively clearing it. Same had to be done with the global assumption context since there was a conflict of scoping.

  Anyways, for the caching at least, I think that the above commit is enough to solve the problems with assumptions-and-caching. From there, we can move on to scoping (I feel that a new thread (with accompanying wiki writeup) is in order).

  Cheers,
   Christian

Aaron S. Meurer

unread,
May 2, 2011, 2:57:29 PM5/2/11
to sy...@googlegroups.com
I agree that Frederik's idea is an interesting one, but we would need to have other people who understand it well if we were to attempt to implement it.  If you could write something up on the wiki, it would go a long way towards this.

Aaron Meurer

Tom Bachmann

unread,
May 3, 2011, 9:00:02 AM5/3/11
to sy...@googlegroups.com
Actually how does this relate to the following wiki page:

https://github.com/sympy/sympy/wiki/Algebras-in-SymPyCore

On 02.05.2011 19:57, Aaron S. Meurer wrote:
> I agree that Frederik's idea is an interesting one, but we would need to
> have other people who understand it well if we were to attempt to
> implement it. If you could write something up on the wiki, it would go a
> long way towards this.
>
> Aaron Meurer
>
> On May 2, 2011, at 11:49 AM, Haz wrote:
>
>> A summary would indeed help. I have another fear though, that this
>> idea (while enticing) is not largely understood (afaik). This puts the
>> burden of re-working SymPy into mainly your hands, Fredrik, where
>> patching SymPy to work with the new assumptions can be jointly tackled
>> (evidenced by the vast number of branches that exist for doing just that).
>>
>> On Mon, May 2, 2011 at 1:42 PM, Tom Bachmann <nes...@googlemail.com
>> <mailto:nes...@googlemail.com>> wrote:
>>
>> On 2 Mai, 18:26, Fredrik Johansson <fredrik.johans...@gmail.com

>> <mailto:fredrik.johans...@gmail.com>>

>> <mailto:sy...@googlegroups.com>.


>> To unsubscribe from this group, send email to
>> sympy+un...@googlegroups.com

>> <mailto:sympy%2Bunsu...@googlegroups.com>.


>> For more options, visit this group at
>> http://groups.google.com/group/sympy?hl=en.
>>
>>
>>
>> --
>> You received this message because you are subscribed to the Google
>> Groups "sympy" group.
>> To post to this group, send email to sy...@googlegroups.com

>> <mailto:sy...@googlegroups.com>.


>> To unsubscribe from this group, send email to
>> sympy+un...@googlegroups.com

>> <mailto:sympy+un...@googlegroups.com>.

Vinzent Steinberg

unread,
May 3, 2011, 11:02:18 AM5/3/11
to sympy
On 3 Mai, 15:00, Tom Bachmann <e_mc...@web.de> wrote:
> Actually how does this relate to the following wiki page:
>
> https://github.com/sympy/sympy/wiki/Algebras-in-SymPyCore

SymPyCore is a split-off project that attempted to radically rewrite
the core. It's basically a one-man project by Pearu. He managed to
achieve impressive speed-ups in pure Python (without caching IIRC). We
should definitely learn from his approaches. Actually it is a (very)
long-term goal to merge back improvements. For more information, see
[1].

Aaron wrote:
> I agree that Frederik's idea is an interesting one, but we would need to have other people who understand it well if we were to attempt to implement it.

I think the best example is mpmath's source code [2].

Vinzent



[1] http://code.google.com/p/sympycore/
[2] http://code.google.com/p/mpmath/source/browse/#svn%2Ftrunk%2Fmpmath

Tom Bachmann

unread,
May 3, 2011, 11:06:41 AM5/3/11
to sy...@googlegroups.com
On 03.05.2011 16:02, Vinzent Steinberg wrote:
> On 3 Mai, 15:00, Tom Bachmann<e_mc...@web.de> wrote:
>> Actually how does this relate to the following wiki page:
>>
>> https://github.com/sympy/sympy/wiki/Algebras-in-SymPyCore
>
> SymPyCore is a split-off project that attempted to radically rewrite
> the core. It's basically a one-man project by Pearu. He managed to
> achieve impressive speed-ups in pure Python (without caching IIRC). We
> should definitely learn from his approaches. Actually it is a (very)
> long-term goal to merge back improvements. For more information, see
> [1].
>

No I mean how does the description of algebras given on that wiki page
relate to the vision you have?

Fredrik Johansson

unread,
May 3, 2011, 11:44:05 AM5/3/11
to sy...@googlegroups.com
On Tue, May 3, 2011 at 3:00 PM, Tom Bachmann <e_m...@web.de> wrote:
> Actually how does this relate to the following wiki page:
>
> https://github.com/sympy/sympy/wiki/Algebras-in-SymPyCore

It's roughly the same thing.

Fredrik

Ondrej Certik

unread,
May 3, 2011, 4:47:17 PM5/3/11
to sy...@googlegroups.com

I also think that what Fredrik says might be a good idea. I don't have
much experience with this to have a clear opinion though. The reason I
have just used Add/Mul/Pow classes for everything in SymPy (long time
ago) is that it is conceptually a super simple idea, and it got us
very far. E.g. from the Zen of Python:

1) Simple is better than complex (Add/Mul/Pow is simpler than all the
algebras+other machinery)
2) Complex is better than complicated (the algebras are probably
better than the complicated entangled assumptions+cache)


As such, I now that we can get very fast just with Add/Mul/Pow (see
the csympy code: https://github.com/certik/csympy), and when using
Refine() and other things, we should be able to have core not using
assumptions nor cache, be fast, and using the new assumptions in
refine(). That fixes the current sympy, using pretty much the same
architecture.

Fredrik+Pearu's approach might be a better approach, looking at things
from a different angle, as Fredrik says, it might not be that
difficult to use. I still think though, that we should first get
assumptions out of the core, and then we can either just use csympy,
or we can switch to Fredrik+Pearu's approach.

Ondrej

Tom Bachmann

unread,
May 3, 2011, 5:02:21 PM5/3/11
to sy...@googlegroups.com
On 03.05.2011 21:47, Ondrej Certik wrote:
> On Tue, May 3, 2011 at 8:44 AM, Fredrik Johansson
> <fredrik....@gmail.com> wrote:
>> On Tue, May 3, 2011 at 3:00 PM, Tom Bachmann<e_m...@web.de> wrote:
>>> Actually how does this relate to the following wiki page:
>>>
>>> https://github.com/sympy/sympy/wiki/Algebras-in-SymPyCore
>>
>> It's roughly the same thing.
>
> I also think that what Fredrik says might be a good idea. I don't have
> much experience with this to have a clear opinion though. The reason I
> have just used Add/Mul/Pow classes for everything in SymPy (long time
> ago) is that it is conceptually a super simple idea, and it got us
> very far. E.g. from the Zen of Python:
>
> 1) Simple is better than complex (Add/Mul/Pow is simpler than all the
> algebras+other machinery)
> 2) Complex is better than complicated (the algebras are probably
> better than the complicated entangled assumptions+cache)
>
>
> As such, I now that we can get very fast just with Add/Mul/Pow (see
> the csympy code: https://github.com/certik/csympy), and when using
> Refine() and other things, we should be able to have core not using
> assumptions nor cache, be fast, and using the new assumptions in
> refine(). That fixes the current sympy, using pretty much the same
> architecture.
>

I don't find that a very convincing argument (which is not saying you
are wrong, of course). Given a specific problem everyone (given enough
time, energy, and general cleverness) can come up with a nice and clean
solution that is also fast. The problem with comparing this to current
sympy is that current sympy does *a lot* more. E.g. all of the core
classes (Mul, Pow etc) treat orders, non-commutative symbols, etc etc.
Now you may rightly argue that this should not be in core, but I suppose
you do not want to throw it away either...

This is why I think the algebras approach is better: there different
algebras can manage expressions of different complexity. So lots of
things that are in current core and slow us down can just become part of
more specialised algebras. Note also that csympy would/could then become
the "core" algebra, achieving a final synthesis of approaches.

Obviously, it's hard to discuss merits without a proper writeup. The
idea of "several algebras" (Fredrik+Pearu) is new to me. I have been
meditating it quite a bit today, and I hope I can come up with something
coherent to discuss tomorrow morning.

Ronan Lamy

unread,
May 3, 2011, 5:24:32 PM5/3/11
to sy...@googlegroups.com

I don't understand that argument. You could just as well say, with
sympy's current design, that different expressions can be implemented by
different classes, etc. The big issue I see with these algebras is that
it creates a design that's more functional than object-oriented and
destroys the identity of objects that belong to several structures (e.g.
in Sage, Integer(1) are different objects).

Ondrej Certik

unread,
May 3, 2011, 8:30:07 PM5/3/11
to sy...@googlegroups.com

Please list the "etc., etc.":

* Non-commutative symbols should not be handled by the core. And if
they do, it's easy to create an NCMul class, as it used to be long
time ago in sympy.

* orders: don't understand this point, why it should cause any problems

> that this should not be in core, but I suppose you do not want to throw it
> away either...

The noncommutative symbols not assumptions are used a lot, except in
special cases here and there in tests. Those tests should be made to
pass obviously, but we should not be constrained by the fact, that we
"can't throw away old assumptions". Christian's branch showed that
indeed that is possible.

Ondrej

Tom Bachmann

unread,
May 4, 2011, 5:37:46 AM5/4/11
to sy...@googlegroups.com
On 02.05.2011 19:57, Aaron S. Meurer wrote:
> I agree that Frederik's idea is an interesting one, but we would need to
> have other people who understand it well if we were to attempt to
> implement it. If you could write something up on the wiki, it would go a
> long way towards this.

I wrote up my view of the algebras model. Obviously the typical
disclaimers apply: I don't know sympy very well, I don't really know
sympycore at all, bla bla. Please comment.

Ondrej, Ronan: I hope this answers your questions as well.

Ronan Lamy

unread,
May 4, 2011, 1:53:26 PM5/4/11
to sy...@googlegroups.com
Thanks for the write-up. It does confirm what I had been thinking: this
model basically amounts to rewriting sympy in a Lispish rather than
Pythonic style - consider, for instance, (ADD, (x, y, 5)) vs Add(x, y,
5). Besides that, I don't see anything that couldn't be done with the
current design, replacing "the object's algebra" with "the object's
class" and with the equivalences Verbatim == Basic, Calculus == Expr,
Algebra == BasicMeta, CachingAlgebra == AssumeMeths, etc. but I'm
probably overlooking something.

Also, I get the impression that this doesn't solve any problem with the
assumptions, while assumptions and caching are an additional problem to
solve for this model.

Fredrik Johansson

unread,
May 4, 2011, 2:09:43 PM5/4/11
to sy...@googlegroups.com

I don't see how this is "more functional than object-oriented". On the
contrary, using a class to encapsulate the notion of an algebra is
more object-oriented than spreading the equivalent code across various
methods in an ad-hoc fashion (for example, having Mul know about lots
of different mathematical objects that can be multiplied) and choosing
between options mostly by passing flags to functions. The code in Sage
is quite clean, and very easy to extend (often easier than doing the
same thing in SymPy).

That's not to say SymPy should necessarily adopt an identical
approach, but it's worth thinking about using explicit objects
(whether they are called "algebras", "rings", "contexts", etc) to
distinguish between different classes of mathematical objects and to
store options.

Whether singleton objects are used for special values like 1 is rather
a trivial issue and completely orthogonal to other design
considerations. AFAIK, the current design exists only for performance
reasons ('is' is much faster than '==' in Python, but this is mostly
irrelevant with C-based types, and it can be worked around fairly
easily in Python anyhow).

Fredrik

Fredrik Johansson

unread,
May 4, 2011, 2:17:59 PM5/4/11
to sy...@googlegroups.com
On Wed, May 4, 2011 at 7:53 PM, Ronan Lamy <ronan...@gmail.com> wrote:
> Le mercredi 04 mai 2011 à 10:37 +0100, Tom Bachmann a écrit :
>> On 02.05.2011 19:57, Aaron S. Meurer wrote:
>> > I agree that Frederik's idea is an interesting one, but we would need to
>> > have other people who understand it well if we were to attempt to
>> > implement it. If you could write something up on the wiki, it would go a
>> > long way towards this.
>>
>> I wrote up my view of the algebras model. Obviously the typical
>> disclaimers apply: I don't know sympy very well, I don't really know
>> sympycore at all, bla bla. Please comment.
>>
>> Ondrej, Ronan: I hope this answers your questions as well.
>>
> Thanks for the write-up. It does confirm what I had been thinking: this
> model basically amounts to rewriting sympy in a Lispish rather than
> Pythonic style - consider, for instance, (ADD, (x, y, 5)) vs Add(x, y,
> 5). Besides that, I don't see anything that couldn't be done with the
> current design, replacing "the object's algebra" with "the object's
> class" and with the equivalences Verbatim == Basic, Calculus == Expr,
> Algebra == BasicMeta, CachingAlgebra == AssumeMeths, etc. but I'm
> probably overlooking something.

These are just implementation details of SympyCore, and they probably
don't belong in the assumptions writeup. How SymPy represents
expressions internally is largely irrelevant to whether one adopts a
parent-element model. In fact much of the point is to allow different
ways to represent data (as SymPy already does with the new polynomial
code).

The starting point is just that all elements have a reference to a
parent, and one way to implement assumptions then would be to make
assumptions a (mutable) property of the parent. The reason the
parent-element model makes sense to discuss in the context of
assumptions is that it provides a natural way to define domains for
symbols (as a "first level" of assumptions) -- for example, if one
wants symbols representing elements of R rather than C by default, one
can use an algebra for this purpose.

Fredrik

Ondrej Certik

unread,
May 4, 2011, 3:25:56 PM5/4/11
to sy...@googlegroups.com
On Wed, May 4, 2011 at 11:09 AM, Fredrik Johansson
<fredrik....@gmail.com> wrote:
[...]

>
> I don't see how this is "more functional than object-oriented". On the
> contrary, using a class to encapsulate the notion of an algebra is
> more object-oriented than spreading the equivalent code across various
> methods in an ad-hoc fashion (for example, having Mul know about lots
> of different mathematical objects that can be multiplied) and choosing
> between options mostly by passing flags to functions. The code in Sage
> is quite clean, and very easy to extend (often easier than doing the
> same thing in SymPy).

Sage uses ginac, which works pretty much just like SymPy (it has Add,
Mul, Pow C++ classes). So a modification of C++ (or Cython) is needed
if changes are made.

Do you know, how ginac fits the algebra structure? It seems to me,
that it is something added on top of the Add/Mul/Pow machinery. So
maybe we can do something similar in sympy.

Ondrej

Aaron S. Meurer

unread,
May 4, 2011, 3:51:57 PM5/4/11
to sy...@googlegroups.com

This is probably the same sort of thing that we worked on in Los Alamos. The core should be rewritten so that it doesn't specifically know about things that combine into other things, like Order, infinity, etc.

See http://code.google.com/p/sympy/issues/detail?id=1941.

I'm also +1 on removing non-commutativity from the core. How should we do it. Should we just have an NCMul class whose first argument is a Mul (or Atom), which would be the non-commutative part?

Aaron Meurer

>
>> that this should not be in core, but I suppose you do not want to throw it
>> away either...
>
> The noncommutative symbols not assumptions are used a lot, except in
> special cases here and there in tests. Those tests should be made to
> pass obviously, but we should not be constrained by the fact, that we
> "can't throw away old assumptions". Christian's branch showed that
> indeed that is possible.
>

> Ondřej
>

Ronan Lamy

unread,
May 4, 2011, 4:06:12 PM5/4/11
to sy...@googlegroups.com
Calling algebra.mul(a, b) instead of a.__mul__(b) is what I mean when I
say "more functional than object-oriented". Now, it's true that
Mul.flatten() is a cesspit of ugliness, but that's precisely because
__mul__ methods call Mul() instead of the other way round.

> That's not to say SymPy should necessarily adopt an identical
> approach, but it's worth thinking about using explicit objects
> (whether they are called "algebras", "rings", "contexts", etc) to
> distinguish between different classes of mathematical objects and to
> store options.

I'm not sure about options, but having explicit objects representing
mathematical types is certainly a good idea. The natural way of doing
that in Python is to use a class.

> Whether singleton objects are used for special values like 1 is rather
> a trivial issue and completely orthogonal to other design
> considerations. AFAIK, the current design exists only for performance
> reasons ('is' is much faster than '==' in Python, but this is mostly
> irrelevant with C-based types, and it can be worked around fairly
> easily in Python anyhow).
>

Singleton objects are useful for more important things than optimising
'==' with 'is'. They represent 'sui generis' objects that have specific
behaviour. But that wasn't my point (not that you could have known it,
given that I forgot half the words in my final parenthesis). I meant
that having Integer(42) and Rational(42) being different, barely related
objects, is unintuitive and makes it hard to extend an existing
structure with additional objects. IIUC, to have something like
S.Infinity in Sage, you'd need to create a
TwoPointCompactifiedExtendedRealLine algebra and convert all your real
objects to it (granted, the last one seems to happen automagically).

Aaron S. Meurer

unread,
May 4, 2011, 4:10:14 PM5/4/11
to sy...@googlegroups.com

+1. When I was working on the doctests in the polys, I was fixing some bugs in the domains, and I noticed that it's much cleaner to have QQ(2, 1) not automatically convert to ZZ(2). The SymPy domain required all kinds of special code to check for singletons and automatic casting like that, which would break if, for example, a new number was made into a singleton.

Aaron Meurer

Ronan Lamy

unread,
May 4, 2011, 5:01:40 PM5/4/11
to sy...@googlegroups.com

We can't really remove non-commutativity from the core. Since Basic
doesn't even have multiplication, it isn't commutative. But we could
make it so that we could assume commutativity in the most used methods.
To do it consistently, we'd need commutative and non-commutative
versions of Expr and each of its subclass. I think the path of least
resistance is rather to create a CommutativeMul class, and have Mul send
all its commutative arguments to it.

Tom Bachmann

unread,
May 4, 2011, 6:52:54 PM5/4/11
to sy...@googlegroups.com

>> Singleton objects are useful for more important things than optimising
>> '==' with 'is'. They represent 'sui generis' objects that have specific
>> behaviour. But that wasn't my point (not that you could have known it,
>> given that I forgot half the words in my final parenthesis). I meant
>> that having Integer(42) and Rational(42) being different, barely related
>> objects, is unintuitive and makes it hard to extend an existing
>> structure with additional objects. IIUC, to have something like
>> S.Infinity in Sage, you'd need to create a
>> TwoPointCompactifiedExtendedRealLine algebra and convert all your real
>> objects to it (granted, the last one seems to happen automagically).
>
> +1. When I was working on the doctests in the polys, I was fixing some bugs in the domains, and I noticed that it's much cleaner to have QQ(2, 1) not automatically convert to ZZ(2). The SymPy domain required all kinds of special code to check for singletons and automatic casting like that, which would break if, for example, a new number was made into a singleton.
>

I think you are actually +1-ing here the opposite of Ronan's statement,
or do I mis-understand what you are saying?

I would agree to that latter interpretation: Integer(1) and Rational(1)
*should* be clearly distinguished, although related objects. This is
even so in "ordinary mathematics", as can be seen with some (admittedly
contrived) questions like "what is the cardinality of 1?".

Aaron S. Meurer

unread,
May 4, 2011, 7:02:45 PM5/4/11
to sy...@googlegroups.com

On May 4, 2011, at 4:52 PM, Tom Bachmann wrote:

>
>>> Singleton objects are useful for more important things than optimising
>>> '==' with 'is'. They represent 'sui generis' objects that have specific
>>> behaviour. But that wasn't my point (not that you could have known it,
>>> given that I forgot half the words in my final parenthesis). I meant
>>> that having Integer(42) and Rational(42) being different, barely related
>>> objects, is unintuitive and makes it hard to extend an existing
>>> structure with additional objects. IIUC, to have something like
>>> S.Infinity in Sage, you'd need to create a
>>> TwoPointCompactifiedExtendedRealLine algebra and convert all your real
>>> objects to it (granted, the last one seems to happen automagically).
>>
>> +1. When I was working on the doctests in the polys, I was fixing some bugs in the domains, and I noticed that it's much cleaner to have QQ(2, 1) not automatically convert to ZZ(2). The SymPy domain required all kinds of special code to check for singletons and automatic casting like that, which would break if, for example, a new number was made into a singleton.
>>
>
> I think you are actually +1-ing here the opposite of Ronan's statement, or do I mis-understand what you are saying?

Well, I guess at least one of us is misunderstanding Ronan. Let's just say that I am +1 to my own statement :)

Aaron Meurer

Tom Bachmann

unread,
May 5, 2011, 4:14:40 AM5/5/11
to sy...@googlegroups.com

> Thanks for the write-up. It does confirm what I had been thinking: this
> model basically amounts to rewriting sympy in a Lispish rather than
> Pythonic style - consider, for instance, (ADD, (x, y, 5)) vs Add(x, y,
> 5).

Well first of all this "lisp-style" is already pretty much there in the
core, disguised as .func and .args attributes. I think there is even a
bug report saying that this information should be used in preference
over class information, which is an implementation detail.

Apart from that I agree, and I would certainly like another way too, I
just didn't see how to come up with one! The underlying issue seems to
me to be that there are various operations (addition etc) that we want
to look uniform over certain (essentially all) classes of objects, but
that have very different implementations. So we *need* a uniform
representation, and S-exprs just seem a very simple solution.

> Besides that, I don't see anything that couldn't be done with the
> current design

Yes. I think the more important question is how painful it is going to
be :-).

> replacing "the object's algebra" with "the object's
> class" and with the equivalences Verbatim == Basic, Calculus == Expr,
> Algebra == BasicMeta, CachingAlgebra == AssumeMeths, etc. but I'm
> probably overlooking something.
>

I'm not sure I understand.

The class of an object in theory determines how it is combined with
others, but in practice that is decided in the Mul, Add etc classes *for
all objects*.

Verbatim == Basic does not seem to work for me because everything
derives from Basic, but not everything is a verbatim. The whole point is
that objects from different algebras can represent themselves as they
wish, they do *not* have to follow the func-args convention.

> Also, I get the impression that this doesn't solve any problem with the
> assumptions, while assumptions and caching are an additional problem to
> solve for this model.
>

Well as far as I understand the problems wrt caching and assumptions in
the current model are:

1) Assumptions are tied in at the lowest level of the core (which
apparently is a problem; I am just accepting that as a fact of life for
now).
2) If we remove assumptions from objects entirely the cache breaks down.
(Apart from there not being a viable transition strategy, as far as I see.)
3) There are certain hacks to fix this like flushing the cache
aggressively, but they remain hacks.
4) At any rate the cache causes lots of headaches and there are calls
for disabling it in general.
5) But that won't work either because our performance depends crucially
on it for some computations.

Having assumptions at a mix-in lower down in the algebra hierarchy
solves (1) [I think], avoids the trouble with (2) and (3). Similarly
having caching as a "mixin algebra" solves (4) without the hacks of (5).

The rest is just sugar to make things look coherent.

Haz

unread,
May 5, 2011, 10:27:31 AM5/5/11
to sy...@googlegroups.com
Well as far as I understand the problems wrt caching and assumptions in the current model are:

1) Assumptions are tied in at the lowest level of the core (which apparently is a problem; I am just accepting that as a fact of life for now).
2) If we remove assumptions from objects entirely the cache breaks down. (Apart from there not being a viable transition strategy, as far as I see.)
3) There are certain hacks to fix this like flushing the cache aggressively, but they remain hacks.
4) At any rate the cache causes lots of headaches and there are calls for disabling it in general.
5) But that won't work either because our performance depends crucially on it for some computations.

Having assumptions at a mix-in lower down in the algebra hierarchy solves (1) [I think], avoids the trouble with (2) and (3). Similarly having caching as a "mixin algebra" solves (4) without the hacks of (5).

  So what is the issue with this solution:

  I don`t see it as too hackish, since it simply shifts the use of the cache from being always on to being used for a single calculation. The solution in the branch from last year was indeed more of a hack (flushed the cache whenever an assumption was added or removed from the global assumption context). If the above solution is adopted, this deals with 2-5 (as far as I can see).

  The big questions that remain are those involving (1): it is generally accepted that the old assumption system should give way to the new one, but to what extent we remove the old ones from the core? How the new ones replace the old ones? What is the syntax that we will use for them? What "assumptions" will we make about the assumption system? (i.e., local -vs- global context by default)

  I'm sorry I derailed the conversation into discussion on the cache (I do feel that the above approach is appropriate).

  Cheers,
   Christian

Aaron Meurer

unread,
May 5, 2011, 12:29:24 PM5/5/11
to sy...@googlegroups.com

Well, one of the biggest problems with having the assumptions in the
core is that any time you want to edit the assumptions you have to
edit the core. Having them separate should make them much more
extensible.

Also, maybe I am wrong, but I think it would be easier to have
something like Ask(expr, Q.whatever, Assume(x>y)) with the new system.
At any rate, this sort of things cannot be done with our current
is_assumption system (how do you assume x>y using that?).

Aaron Meurer

>   I'm sorry I derailed the conversation into discussion on the cache (I do
> feel that the above approach is appropriate).
>   Cheers,
>    Christian
>

Ronan Lamy

unread,
May 5, 2011, 12:35:34 PM5/5/11
to sy...@googlegroups.com
Le jeudi 05 mai 2011 à 10:27 -0400, Haz a écrit :
> Well as far as I understand the problems wrt caching and
> assumptions in the current model are:
>
> 1) Assumptions are tied in at the lowest level of the core
> (which apparently is a problem; I am just accepting that as a
> fact of life for now).
> 2) If we remove assumptions from objects entirely the cache
> breaks down. (Apart from there not being a viable transition
> strategy, as far as I see.)
> 3) There are certain hacks to fix this like flushing the cache
> aggressively, but they remain hacks.
> 4) At any rate the cache causes lots of headaches and there
> are calls for disabling it in general.
> 5) But that won't work either because our performance depends
> crucially on it for some computations.
>
> Having assumptions at a mix-in lower down in the algebra
> hierarchy solves (1) [I think], avoids the trouble with (2)
> and (3). Similarly having caching as a "mixin algebra" solves
> (4) without the hacks of (5).

If you mean something like
https://github.com/rlamy/sympy/tree/refactor-assumptions , it can be
done, but doesn't buy us much.

> So what is the issue with this solution:
> - https://github.com/haz/sympy/commit/fb750ea49b963fa12d3a3e2dc833e53931050160
>
>
> I don`t see it as too hackish, since it simply shifts the use of the
> cache from being always on to being used for a single calculation. The
> solution in the branch from last year was indeed more of a hack
> (flushed the cache whenever an assumption was added or removed from
> the global assumption context). If the above solution is adopted, this
> deals with 2-5 (as far as I can see).

That's very much a hack. It makes top-level calls behave differently
from inner calls, in a way that's hard to control. It's still wrong if
assumptions change within a top-level call. And, since it uses global
state, it'll break down completely in presence of threading (the rest of
sympy isn't likely to be thread-safe either, though) or if an exception
is raised.

> The big questions that remain are those involving (1): it is
> generally accepted that the old assumption system should give way to
> the new one, but to what extent we remove the old ones from the core?
> How the new ones replace the old ones? What is the syntax that we will
> use for them? What "assumptions" will we make about the assumption
> system? (i.e., local -vs- global context by default)
>

For the syntax, see http://code.google.com/p/sympy/issues/detail?id=2338

> I'm sorry I derailed the conversation into discussion on the cache
> (I do feel that the above approach is appropriate).

I think that the cache is a critical roadblock, so it's a good thing
that we discuss it.

Ondrej Certik

unread,
May 5, 2011, 2:25:12 PM5/5/11
to sy...@googlegroups.com


I think that the biggest problem is that the assumptions are standing
in the way to optimize the speed of the core. Ginac doesn't use any
assumptions, neither does csympy (http://github.com/certik/csympy).
Since Sage shows that it is not needed, we should follow the same
route. And figure out some way to use assumptions in addition to the
core somehow.

Ondrej

Alexey U. Gudchenko

unread,
May 5, 2011, 2:42:39 PM5/5/11
to sy...@googlegroups.com
30.04.2011 02:16, Aaron S. Meurer пишет:
> So Ondřej, Christian, and I talked about this before GSoC, but I
> forgot to mention it on the list. We need to figure out, as a
> community, how to remove the old assumptions system and merge in the
> new one. We have already had several branches attempting to do it,
> and we couldn't even get a GSoC to do it because we don't really have
> a clear strategy on how to do it.
>
> So I think what we need to do is edit this wiki page
> (https://github.com/sympy/sympy/wiki/Assumptions) and under the
> "Approaches" section, but your ideas on how to do it, along with a
> list of pros and cons, like
>
> Idea: Describe the idea.
>
> Pros: List the pros.
>
> Cons: List the cons.
>
> And edit each idea with the pros and cons that you see with it. And
> then from there hopefully we will be able to see which way will be
> the best, or at least it will be easier to make a decision about it.
>
> By the way, the examples on that page need to be removed, or
> refactored to fit the above form. The page was hijacked from
> something else, so probably they should just be removed.
>
> Aaron Meurer
>

Hi all!

Sorry, I have not can to respond immediately. But I read print listing
of the this topic yesterday,

I wonder that no one havn't answer what are aims of the assumptions and
what are the assumptions designed for?

May be it is clear for all, but I think it is mistake if we omit this
step and begin with construction of architecture. In complex cases the
design and architecture depend on the aims and use cases of assumptions
system. If we invite aims, (and only then the descriptions syntax and
use cases) only then we can think how to handle with them more
efficiently. An what restrictions are,
In general (for many systems: languages, platforms, ) the restrictions
define the syntax and use-cases too.

(And assumptions can be splitted and classified - for some of them one
strategy is needed, for other types of assumptions - another)

Also I think it is strategic mistake if we invite general description of
assumptions and only then will decide how to handle with all of them in
the core.

May be someone oriented on the others CAS's system, but I warn that they
are not the etalons and may be have (traditional historical) mistakes of
design (how it was for series). But they can take into account too.

Well, when I have concerned the assumptions, I noticed, first of all,
that they are used for this users aims:

(a) simplification.
Assumption help us to simplify expressions for the user.
he typical example:

>>> sqrt(x**2)
abs(x) # x is Real
>>> _
x # x is positive.

Complex case (related with expression):
>>> sqrt((x + y)**2)
abs(x + y) # (x + y) is Real
>>> _
(x + y) # (x + y) is positive.

BTW. It would be good when the CAS can analyze some complex expression,
and offer to the user what assumptions can simplify those expressions.

>>> how_to_simplify(sqrt(x**2))
[assume that x is positive: then sqrt(x**2) --> x, ...]


BTW, the assumption `x is positive` have nothing better than `x is
grater then, saying, 7`. The reasons of its existence are the same.

BTW, `is_positive` assumptions have special classification type (it is
distinguished from assumption `is_zero` which related with another sort
of assumption).


(b) Mathematical assumptions. The typical example: whether we are on
some field or on another.

BTW, This construction can resolve problem to ask assumption system
permanently during the calculations.
if we have some nested or sets of general function {`A`, `B`, `C`}, and
every of them internally analaze the assumptions and have code blockes
like "A: A_complex if arg complex, A_real, if arg real...", so they can
grouped to sets {`A_real`, `B_real`, `C_real`} without internal asking
about assumptions.

One can say that those methods has domain parameter, like in Sage, or in
Polys in SymPy (if I am not mistaken).

I have sowen that Tom already added at the wiki page about connection
asuumptions with algebras (That's good).

(c) In general, especially when we consider them from programmatic side
(not mathematically) Assumptions plays the role of rules how to
calculate or simplify expressions. Nothing else.

The typical example is:
>>> 1/0
oo (assume that we are deal with reals)
>>> 1/0
zoo (assume that we in complex)

(In this case an assumption must not be related with symbols)

I noticed that (c) (assumtions are the rules not only for symbols, but
for operations too, how to deal with them) is a general case of the
assumption system. Is not it? But we must catch the balanse how to
describe assumtions

From it immediately followed that it is not quite correct:

01.05.2011 23:35 +0400, Tom Bachmann
> Every assumption is about symbols.

I can't discuss further about the algorithms, what to do with branches,
and how it related with cache system without resolving those questions:
precise description of assumptions aims, working-off of the use cases
(and resolving some logical problems which exists in the current system
or can be raised in the future).

P.S.
About "stopping any development by Ronan's attitude" and hanging of
branches.
I think that we must do not ignore the strategic decisions.
I understand that the project must be on the development. But if the new
code (more and more new code) which is based on the bad strategic
decision, then it is meaningless at whole after all.


"If we have a bad strategy and a bad tactic then we lose a battle
rapidly. If we have a bad strategy and the best tactic then we lose a
battle as time goes by. If we have bad tactic and good strategy then we
win a battle finally. And if we have a good tactic and good strategy
then we win a battle rapidly."

If there are difficulties with strategic core decisions then first of
all we must recognize them.

And may be they must be published on the wiki page or in mailing list too:

The offer:
- the arguments for.
- the arguments contra.

before to come to a conclusion or before the decision.

--
Alexey U.

Haz

unread,
May 5, 2011, 3:00:00 PM5/5/11
to sy...@googlegroups.com
On Thu, May 5, 2011 at 12:29 PM, Aaron Meurer <asme...@gmail.com> wrote:
>   The big questions that remain are those involving (1): it is generally
> accepted that the old assumption system should give way to the new one, but
> to what extent we remove the old ones from the core? How the new ones
> replace the old ones? What is the syntax that we will use for them? What
> "assumptions" will we make about the assumption system? (i.e., local -vs-
> global context by default)

Well, one of the biggest problems with having the assumptions in the
core is that any time you want to edit the assumptions you have to
edit the core.  Having them separate should make them much more
extensible.

  It's worth picking apart that sentiment. When I say "to what extent we remove the old ones from the core?", I am considering the fact that is_positive may still remain as a function of Expr. The fact that it internally calls an ask(...) of the new assumption system is besides the point.
 
Also, maybe I am wrong, but I think it would be easier to have
something like Ask(expr, Q.whatever, Assume(x>y)) with the new system.
 At any rate, this sort of things cannot be done with our current
is_assumption system (how do you assume x>y using that?).

  These are a level of complex assumptions that are out of the scope of what the new assumption system can handle. It requires a higher form of reasoning (potentially FOL, etc).

  Cheers

Ronan Lamy

unread,
May 5, 2011, 3:07:27 PM5/5/11
to sy...@googlegroups.com

It's hard to compare between systems with different design philosophies,
and even harder to compare a real system with a toy system. Sympy shows
that coercing all the time isn't needed. Does that mean that Sage should
get rid of coercions? I still don't know how exactly the old assumptions
prevent us from optimising the current core, though I do have ideas to
improve it (use dicts for commutative expressions, clear up the overhead
of expand()). Is there any possible improvement (not redesign) of the
current design that is impossible because of assumptions?

Haz

unread,
May 5, 2011, 3:07:44 PM5/5/11
to sy...@googlegroups.com
On Thu, May 5, 2011 at 12:35 PM, Ronan Lamy <ronan...@gmail.com> wrote:
>   So what is the issue with this solution:
> - https://github.com/haz/sympy/commit/fb750ea49b963fa12d3a3e2dc833e53931050160
>
>
>   I don`t see it as too hackish, since it simply shifts the use of the
> cache from being always on to being used for a single calculation. The
> solution in the branch from last year was indeed more of a hack
> (flushed the cache whenever an assumption was added or removed from
> the global assumption context). If the above solution is adopted, this
> deals with 2-5 (as far as I can see).

That's very much a hack. It makes top-level calls behave differently
from inner calls, in a way that's hard to control. It's still wrong if
assumptions change within a top-level call. And, since it uses global
state, it'll break down completely in presence of threading (the rest of
sympy isn't likely to be thread-safe either, though) or if an exception
is raised.

  Threading has never been a requirement (as far as I am aware of) at making these decisions. The correctness of the system should not be affected by when the cache is used (or the cache is being used incorrectly). I think it's perfectly reasonable to regulate when the cache is used for the top level calls.

  Alternatively, we can simply stipulate that the cache is off by default, and only enabled when needed (e.g., when a gruntz calculation begins).

>   The big questions that remain are those involving (1): it is
> generally accepted that the old assumption system should give way to
> the new one, but to what extent we remove the old ones from the core?
> How the new ones replace the old ones? What is the syntax that we will
> use for them? What "assumptions" will we make about the assumption
> system? (i.e., local -vs- global context by default)
>
For the syntax, see http://code.google.com/p/sympy/issues/detail?id=2338

  Aye, I like that shift in syntax. But I'm speaking more of the whether or not we include assumptions in Symbol creation, whether or not we pass around contexts, whether or not assumptions are even consulted outside of refine / ask, etc.

  Cheers

Tom Bachmann

unread,
May 5, 2011, 3:18:26 PM5/5/11
to sy...@googlegroups.com
First of all thanks alexey, for steering the discussion into an
important direction!

I think that my statement is true, or at least should be true.
Everything that is not a symbol (or involves a symbol) is some concrete
mathematical entity, and all of its properties are settled (in
principle, in practice it might be hard to find out if a certain number
is e.g. positive, even if it is specified uniquely).

So basically I'm saying that things like "I'm an integer" or "I'm
commutative" are not really assumptions, they are type information if
you will. Integer(1) is not the same as Rational(1) is not the same as
eye (identity matrx).

So in particular ExtendedReal(1)/ExtendedReal(0) = oo,
ExtendedComplex(1)/ExtendedComplex(0) = zoo, Real(1)/Real(0) = nan, or
perhaps even an exception [unless we do some auto-conversion].

So we then have the following objects:

* algebras (or domains, or whatever) represent collections that are
typically manipulated together
* concrete elements of algebras. These are definite mathematical
objects, all of whose properties are known in principle.
* symbolic elements. These are elements which contain free symbols, and
not all of whose properties are known

Notice that every element that we ever compute with belongs to some
algebra. All assumptions are about "symbolic elements". They can be used
to do transformations of the expressions that would not be allowed for
all possible values of the free symbols, usually with the intent of
giving the user a better answer to a query [actually this seems a bit
vacuous ...].

Notice also that not everyhing that looks like a symbol is a symbolic
element (this is probably obvious to everyone but me): in e.g. a
polynomial algebra the generator X is a definite entity and *not* a free
symbol. Every statement that makes sense in this algebra is known about
X. A symbolic element would be a parametric family of polynomials,
something like X**2 + p*X + q. Here the p and q are symbols that
represent elements of the coefficient ring. It is sensible to make
assumptions about them, e.g. p > 0 or "X**2 + p*X + q is irreducible" or
whatever.

How is this distinction reflected in the implementation of polys (if at
all)?

Tom Bachmann

unread,
May 5, 2011, 3:26:36 PM5/5/11
to sy...@googlegroups.com
> > Also, maybe I am wrong, but I think it would be easier to have
> > something like Ask(expr, Q.whatever, Assume(x>y)) with the new system.
> At any rate, this sort of things cannot be done with our current
> is_assumption system (how do you assume x>y using that?).
>

I think aaron's point is that the new system can at least express them,
so there is a chance of implementing it later.

>
> These are a level of complex assumptions that are out of the scope of
> what the new assumption system can handle. It requires a higher form of
> reasoning (potentially FOL, etc).
>
> Cheers
>

Tom Bachmann

unread,
May 5, 2011, 3:31:51 PM5/5/11
to sy...@googlegroups.com
> The correctness of the system should not be
> affected by when the cache is used (or the cache is being used
> incorrectly). I think it's perfectly reasonable to regulate when the
> cache is used for the top level calls.

I think that's a contradiction. Either caching is transparent or it
isn't. If it's transparent (and I agree it should be), then regulation
should not be necessary.

Tom Bachmann

unread,
May 5, 2011, 3:35:45 PM5/5/11
to sy...@googlegroups.com
On 05.05.2011 17:35, Ronan Lamy wrote:
> Le jeudi 05 mai 2011 � 10:27 -0400, Haz a �crit :

>> Well as far as I understand the problems wrt caching and
>> assumptions in the current model are:
>>
>> 1) Assumptions are tied in at the lowest level of the core
>> (which apparently is a problem; I am just accepting that as a
>> fact of life for now).
>> 2) If we remove assumptions from objects entirely the cache
>> breaks down. (Apart from there not being a viable transition
>> strategy, as far as I see.)
>> 3) There are certain hacks to fix this like flushing the cache
>> aggressively, but they remain hacks.
>> 4) At any rate the cache causes lots of headaches and there
>> are calls for disabling it in general.
>> 5) But that won't work either because our performance depends
>> crucially on it for some computations.
>>
>> Having assumptions at a mix-in lower down in the algebra
>> hierarchy solves (1) [I think], avoids the trouble with (2)
>> and (3). Similarly having caching as a "mixin algebra" solves
>> (4) without the hacks of (5).
>
> If you mean something like
> https://github.com/rlamy/sympy/tree/refactor-assumptions , it can be
> done, but doesn't buy us much.
>

Can you describe what you have done there? I had a look at the code but
chances are I'm missing important points.

Why does this not buy us much? If I understand correctly you moved the
assumptions logic to a mixin. This sounds like a first step to me to
having swappable assumptions implementations, and indeed no-op
assumptions if this is desirable.

>> So what is the issue with this solution:
>> - https://github.com/haz/sympy/commit/fb750ea49b963fa12d3a3e2dc833e53931050160
>>
>>
>> I don`t see it as too hackish, since it simply shifts the use of the
>> cache from being always on to being used for a single calculation. The
>> solution in the branch from last year was indeed more of a hack
>> (flushed the cache whenever an assumption was added or removed from
>> the global assumption context). If the above solution is adopted, this
>> deals with 2-5 (as far as I can see).
>
> That's very much a hack. It makes top-level calls behave differently
> from inner calls, in a way that's hard to control. It's still wrong if
> assumptions change within a top-level call.

I agree (quite strongly).

Ronan Lamy

unread,
May 5, 2011, 4:44:46 PM5/5/11
to sy...@googlegroups.com
Le jeudi 05 mai 2011 à 20:35 +0100, Tom Bachmann a écrit :
> On 05.05.2011 17:35, Ronan Lamy wrote:

Well, I meant that it's only a first step. I was trying to solve a
different problem: the new assumptions require logic, but the (new)
logic classes derive from Basic, so in order to replace the old
assumptions with the new ones, nothing in Basic or sympy.logic should
depend on the old assumptions (that, BTW, was also the fundamental
reason for the Basic/Expr split).

So, the only changes directly relevant to what we're discussing are
indeed that I moved all the assumption code to
sympy/core/assumptions.py, but it's more than just a mixin, there's also
a metaclass and a few module-global parameters.

I hadn't thought of swappable implementations, actually. I guess my
branch would be easier to follow if I reorganised it with that in mind.
I could also crank up the magic a bit so that a single __metaclass__
definition or class decorator (in 2.6+) would be enough to define
assumptions.


Chris Smith

unread,
May 6, 2011, 10:29:01 AM5/6/11
to sy...@googlegroups.com
Regarding caching...is the use of @memoize any better than @cacheit?

Ondrej Certik

unread,
May 6, 2011, 10:39:08 AM5/6/11
to sy...@googlegroups.com
On Fri, May 6, 2011 at 7:29 AM, Chris Smith <smi...@gmail.com> wrote:
> Regarding caching...is the use of @memoize any better than @cacheit?

I think it's essentially the same. However, if the cache is just a
dictionary, as it is now, then it is a *bad* idea. Here is some intro
into caching:

http://en.wikipedia.org/wiki/Cache_algorithms

it's highly nontrivial. I would be much more happy, if we don't use
any cache by default in the core, only as an optional feature that the
user can turn on, if he is ok with the possible consequences (possibly
unlimited grow in memory size, unless a better cache is implemented,
that gets this under control, which I think is far from trivial as
well).

Ondrej

Ronan Lamy

unread,
May 6, 2011, 12:32:14 PM5/6/11
to sy...@googlegroups.com

For me, (c) isn't part of the assumption system. It's rather an
interpreter setting that should only be used by client code and not
within sympy (counting isympy as client code). Also, I'm not sure if
that's exactly what you're suggesting, but having Integer(1)/Integer(0)
return different results based on context is bad - for exactly the same
reasons as Basic.keep_sign.

> >> From it immediately followed that it is not quite correct:
> >
> > 01.05.2011 23:35 +0400, Tom Bachmann
> >> Every assumption is about symbols.
> >
>
> I think that my statement is true, or at least should be true.
> Everything that is not a symbol (or involves a symbol) is some concrete
> mathematical entity, and all of its properties are settled (in
> principle, in practice it might be hard to find out if a certain number
> is e.g. positive, even if it is specified uniquely).
>
> So basically I'm saying that things like "I'm an integer" or "I'm
> commutative" are not really assumptions, they are type information if
> you will. Integer(1) is not the same as Rational(1) is not the same as
> eye (identity matrx).
>
> So in particular ExtendedReal(1)/ExtendedReal(0) = oo,
> ExtendedComplex(1)/ExtendedComplex(0) = zoo, Real(1)/Real(0) = nan, or
> perhaps even an exception [unless we do some auto-conversion].
>
> So we then have the following objects:
>
> * algebras (or domains, or whatever) represent collections that are
> typically manipulated together
> * concrete elements of algebras. These are definite mathematical
> objects, all of whose properties are known in principle.
> * symbolic elements. These are elements which contain free symbols, and
> not all of whose properties are known

There are more collection-like concepts than that. Lumping them all
together under the name "algebra" (or whatever) is a recipe for
conflicting expectations and bad design. I can think of:
* the Python type of objects
* the interface of objects. That's not always formalised in Python, but
when it is, it's a base class, possibly abstract.
* algebraic structures like groups, rings, fields
* mathematical sets - note that the same set can have different
structures applied over it, for instance (ZZ, +) is a group, (ZZ, +, *)
is a ring.

>
> Notice that every element that we ever compute with belongs to some
> algebra.

Here's our fundamental disagreement, I think. Objects aren't
intrinsically part of an algebra, they happen to belong to some set over
which some algebraic structure is defined and used.

> All assumptions are about "symbolic elements". They can be used
> to do transformations of the expressions that would not be allowed for
> all possible values of the free symbols, usually with the intent of
> giving the user a better answer to a query [actually this seems a bit
> vacuous ...].

I suppose that the distinction is between formulas with free symbols and
closed formulas. The former are assumptions, the latter "mathematical
facts".


>
> Notice also that not everyhing that looks like a symbol is a symbolic
> element (this is probably obvious to everyone but me): in e.g. a
> polynomial algebra the generator X is a definite entity and *not* a free
> symbol. Every statement that makes sense in this algebra is known about
> X. A symbolic element would be a parametric family of polynomials,
> something like X**2 + p*X + q. Here the p and q are symbols that
> represent elements of the coefficient ring. It is sensible to make
> assumptions about them, e.g. p > 0 or "X**2 + p*X + q is irreducible" or
> whatever.

+1

> How is this distinction reflected in the implementation of polys (if at
> all)?

AFAIK, at the public interface level, it's not. Instances of Poly can
only represent polynomial expressions, not true polynomials.

Alexey U. Gudchenko

unread,
May 6, 2011, 6:16:19 PM5/6/11
to sy...@googlegroups.com
06.05.2011 20:32, Ronan Lamy пишет:

It depends on the point of view what to consider assumption system.
If I look on the expression like (a la Mathematica, or new assumption
system):

In[0]: simplify( sqrt(x**2), (x>0) | (x is Real) )

Then I see: (1) assumptions on the right side after comma.
Yes, it seems that the assumptions are connected to the variable `x` only.
But I also see, (2) that they act on the first part `sqrt(x**2)`.
In other words, this sort of assumptions are defined with connection to
the variable, but the whole rule (association) connected both to
variable and `sqrt(x**2)`. From programmer point of view I see the rule.

If I look on the old system then I see the same:

In[0]: x = Symbol("x", positive=True)

In[1]: sqrt(x**2)

with the difference that the assumptions acts to the followed
expressions (so they defined the rules how do deal with those expressions).

That is what I meant upon the (c).

Why do I rise this question - the implementation is differ if we connect
assumption only to symbols or of we consider rules (caching, algorithms,
global/local problems)


> Also, I'm not sure if
> that's exactly what you're suggesting, but having Integer(1)/Integer(0)
> return different results based on context is bad - for exactly the same
> reasons as Basic.keep_sign.
>

Tom have clear it. I have used words when describe assumption, Tom -
`ExtendedReal` syntax which precise defines how to deal and with what to
deal. (one variant), so there is no context.

Another question what to do with the S(1) function.

>>>> From it immediately followed that it is not quite correct:
>>>
>>> 01.05.2011 23:35 +0400, Tom Bachmann
>>>> Every assumption is about symbols.
>>>
>>
>> I think that my statement is true, or at least should be true.
>> Everything that is not a symbol (or involves a symbol) is some concrete
>> mathematical entity, and all of its properties are settled (in
>> principle, in practice it might be hard to find out if a certain number
>> is e.g. positive, even if it is specified uniquely).
>>
>> So basically I'm saying that things like "I'm an integer" or "I'm
>> commutative" are not really assumptions, they are type information if
>> you will. Integer(1) is not the same as Rational(1) is not the same as
>> eye (identity matrx).
>>
>> So in particular ExtendedReal(1)/ExtendedReal(0) = oo,
>> ExtendedComplex(1)/ExtendedComplex(0) = zoo, Real(1)/Real(0) = nan, or
>> perhaps even an exception [unless we do some auto-conversion].
>>

Yes, it is a variant. Which can be classified as Mathematical
assumptions (whether we are on some field/ring or on another).
But may be the variants of algorithms and use-cases.
E.g.

- can be used automatic type conversion if possible:

ExtendedReal(1)/0 => ExtendedReal(1)/ExtendedReal(0) => (use
ExtendedReal.div) => oo.

- specifity of field:
1/0 ( in ExtendedReal) => ExtendedReal(1)/ExtendedReal(0) => oo.

At the wiki page, as I understand, the algorithm is described (and it
seems that it is related with hierarchy of algebras as Frederik mention
while he write about parent algebra).

"In order to allow conversion between algebras, every algebra needs to
be able to convert its elements into elements of Verbatim, and it needs
to provide a general method to construct its own representations of a
given S-expr. For this to make sense the possible heads have to be
standardised somewhat, but not completely: while (probably) every
algebra needs to represent addition and should use ADD for that, not
every algebra knows about the sine function. In general if compatibility
with other algebras is not important, private identifiers can be used
for the heads. If an algebra encounters an unrecognised head, it can
simply throw an exception."

The rest of the letter I don't undersatand becouse the issues are
difficult for me (without examples of use-cases and aims of assumptions)

>> So we then have the following objects:
>>
>> * algebras (or domains, or whatever) represent collections that are
>> typically manipulated together
>> * concrete elements of algebras. These are definite mathematical
>> objects, all of whose properties are known in principle.
>> * symbolic elements. These are elements which contain free symbols, and
>> not all of whose properties are known
>
> There are more collection-like concepts than that. Lumping them all
> together under the name "algebra" (or whatever) is a recipe for
> conflicting expectations and bad design. I can think of:
> * the Python type of objects
> * the interface of objects. That's not always formalised in Python, but
> when it is, it's a base class, possibly abstract.
> * algebraic structures like groups, rings, fields
> * mathematical sets - note that the same set can have different
> structures applied over it, for instance (ZZ, +) is a group, (ZZ, +, *)
> is a ring.
>
>>
>> Notice that every element that we ever compute with belongs to some
>> algebra.
>
> Here's our fundamental disagreement, I think. Objects aren't
> intrinsically part of an algebra, they happen to belong to some set over
> which some algebraic structure is defined and used.
>

If I understand correctly, we concern the fundamental question what are
the cause and what corollary.
We can define and deal with algebra and then invite representation for
it e.g. with the help of objects with some properties and behaiviour.
Or we can invite some object and operation with them then analize
algebraic properties from it.
I suppose that the last is more extentable and interesting (at least for
me) for mathematical research (especialy when we can invite miltivariant
algebra).


--
Alexey U.

Alexey U. Gudchenko

unread,
May 6, 2011, 7:22:22 PM5/6/11
to sy...@googlegroups.com
05.05.2011 23:18, Tom Bachmann пишет:


Added on the wiki page:

## The aims of assumptions
### for end-user.

To simplify expressions:

# positive, even
>>> refine(Abs(x), Assume(x, Q.positive))
x
>>> refine(sqrt(x**2), Assume(x, Q.positive))
x
>>> refine((-1)**(x+y), Assume(x, Q.even))
(-1)**y
# algebraic fields
>>> refine(sqrt(x**2), Assume(x, Q.real))
Abs(x)
# mixed
>>> refine(exp(pi*I*2*(x+Rational(1,4))), Assume(x, Q.integer))
I

### for core

Use some logic and facts to implement calculation in convenient mode.

`.is_bounded`, `is_infinity`, `is_zero` and so on.


## Classification of assumptions

- related with symbols for simplification, e.g. `Q.positive`, `Q.even`.
- related with algebraic fields/rings, e.g. `Q.real`, `Q.complex`.
- related with some facts, e.g. `.is_bounded`, `is_infinity`, `is_zero`
and so on.

Alexey U. Gudchenko

unread,
May 6, 2011, 8:09:49 PM5/6/11
to sy...@googlegroups.com
02.05.2011 20:25, Ronan Lamy пишет:
> Le dimanche 01 mai 2011 à 23:37 -0700, Ondrej Certik a écrit :

>>
>> You should assume it. Non commutative symbols are just making things
>> very complicated, and it was a mistake to add them into the core of
>> sympy. They should start in sympy.quantum, and only after they are
>> being used a lot, we should start thinking how to handle them
>> properly.
>
> I agree on this. Non-commutative Symbols don't make a lot of sense. If
> we didn't have them (and severed the inheritance from Boolean), we could
> say that Symbols are complex variables. Instead, we have a murky
> definition.
> Anyway, is_commutative is just an extreme example. Most .__new__()
> and .eval() methods check one assumption or other. We'd have to throw
> all that code away and rewrite it from scratch.
>


Non-commutative Symbols are generalization, commutative Symbols are
specialization. (Remarking that it is a pure mathematica, not a quantum
- because there are no physical lows used when we consider
Non-commutative Symbols),
but I agree that temporary it can be placed in sympy.quantum for tactic
aims.

In any case it would be convinient (and for effective core - to avoid
unnesesary checking) to isolate in the core the possibility to work with
commutative Symbols and possibility to work with non-commutative Symbols
(or even with non-associative symbols). Now, as I understand, in the
SymPy core the non-commutative core is not isolated, and at the same
time by defult the is_commutative is switch off (False) - but many
unnecessary checking are used further and we continue to deal with
commutative.

So the isolation is needed in any case (effective core) - even after "we
should start thinking how to handle them properly".

May be the interface of objects can be used for this.

This type of assumption is classified as related with algebraic
fields/rings. (For this type of assumptions may be meaningless to attach
assumption to symbols at whole )

--
Alexey U.

Ronan Lamy

unread,
May 6, 2011, 8:40:30 PM5/6/11
to sy...@googlegroups.com
Le samedi 07 mai 2011 à 04:09 +0400, Alexey U. Gudchenko a écrit :
> 02.05.2011 20:25, Ronan Lamy пишет:
> > Le dimanche 01 mai 2011 à 23:37 -0700, Ondrej Certik a écrit :
>
> >>
> >> You should assume it. Non commutative symbols are just making things
> >> very complicated, and it was a mistake to add them into the core of
> >> sympy. They should start in sympy.quantum, and only after they are
> >> being used a lot, we should start thinking how to handle them
> >> properly.
> >
> > I agree on this. Non-commutative Symbols don't make a lot of sense. If
> > we didn't have them (and severed the inheritance from Boolean), we could
> > say that Symbols are complex variables. Instead, we have a murky
> > definition.
> > Anyway, is_commutative is just an extreme example. Most .__new__()
> > and .eval() methods check one assumption or other. We'd have to throw
> > all that code away and rewrite it from scratch.
> >
>
>
> Non-commutative Symbols are generalization, commutative Symbols are
> specialization.

I agree with that, in principle. But above, I meant "Symbols" in the
sense of "instances of the class Symbol" and commented on the way these
objects are actually used now. One of our fundamental problems is that
this very important class is misnamed and not defined clearly.

Alexey U. Gudchenko

unread,
May 7, 2011, 5:55:36 AM5/7/11
to sy...@googlegroups.com

Added in introduction section:

"As we have current situation (two systems, a few branches which
improved them). So we collect the description of them, and describe
variants what to do with them on optimal ways (Approaches section).

But at the same time we must keep in the mind the ideal solution of the
assumption system (Target vision section)."

P.S.
The first paragraph is a source of the current thread aims which are
declared originally at the first message. Nevertheless, the second
paragraph have meaning, inspite of that it might be out of topic, or
even can't be resolved before summer, or even in this year. I think this
paragrapgh (an corresponding section) can help to develop assumtion
system in optimal mode (and do not bury problems to land)

Also for description of the current situation and for orientation
between the branches I call everyone to describe shortly the current set
of branches (section Current branches description), which are was
discussed in this thread.


Alexey U.

Alexey U. Gudchenko

unread,
May 7, 2011, 6:50:26 AM5/7/11
to sy...@googlegroups.com
05.05.2011 23:18, Tom Bachmann пишет:
>
> I think that my statement is true, or at least should be true.
> Everything that is not a symbol (or involves a symbol) is some concrete
> mathematical entity, and all of its properties are settled (in
> principle, in practice it might be hard to find out if a certain number
> is e.g. positive, even if it is specified uniquely).
>
> So basically I'm saying that things like "I'm an integer" or "I'm
> commutative" are not really assumptions, they are type information if
> you will. Integer(1) is not the same as Rational(1) is not the same as
> eye (identity matrx).


I have tried to classify assumptions (I oriented and based on the
current usage of them, there is why I do not know what "are really
assumptions" or not, but it has a sense to make more accurate with this
question, of course):

"""
- related with symbols for simplification, e.g. Q.positive, Q.even.
- related with algebraic fields/rings, e.g. Q.real, Q.complex.
- related with some facts, e.g. .is_bounded, is_infinity, is_zero and so
on. They are help us to operate with calculation in core. Apparently
they are derived from the above other classes of assumptions (while
start-upping of the objects) (in this case e.g. is_zero is means that it
is a Zero for the ring). Or they can be derived from expression's
analyze: in this case we can create some class of assumption that are
calculated (in this case is_zero can be means but it this sort of
calculation is a difficult so called "zero test" problem). In any case
we can realize in mind with what we deal exactly (now in the core
somewhere we used is_zero in second sense)."""

May be updates will be for this, but I can offhand to think about
algorithms.

I see that the algorithm and syntax for the first class has no
difficulties apparantly. It is convinient to describe them locally
linked to symbols (or to the expression), and algorithm can be recurrent
to transform expression.

Yes, the second class is a type of information. The variants are how to
deal with this information. Obviously:
- We can also recurently analize this type of information for symbols.
- We can make automatic type conversion recurrently
(in binary mode):
Integer(1) + Integer(2) + Rational(7) ==>
Integer(3) + Rational(7) =>
Rational(10)
(at whole ADD arguments):
Integer(1) + Integer(2) + Rational(7) ==>
Rational(1) + Rational(2) + Rational(7) ==>
Rational(10)

- we can derive resulted field assumption/information of expression
from arguments:
(Integer(1) + Rational(1) is a Q.Rational)

(So we have field assumption/information attached to the
expression, taking into account yours remark that "not all of whose
properties are known")


> So in particular ExtendedReal(1)/ExtendedReal(0) = oo,
> ExtendedComplex(1)/ExtendedComplex(0) = zoo, Real(1)/Real(0) = nan, or
> perhaps even an exception [unless we do some auto-conversion].
>
> So we then have the following objects:
>
> * algebras (or domains, or whatever) represent collections that are
> typically manipulated together
> * concrete elements of algebras. These are definite mathematical
> objects, all of whose properties are known in principle.
> * symbolic elements. These are elements which contain free symbols, and
> not all of whose properties are known

For the last "not all of whose properties are known" I dont clear
imagine an example though.

>
> Notice that every element that we ever compute with belongs to some
> algebra. All assumptions are about "symbolic elements". They can be used
> to do transformations of the expressions that would not be allowed for
> all possible values of the free symbols, usually with the intent of
> giving the user a better answer to a query [actually this seems a bit
> vacuous ...].
>
> Notice also that not everyhing that looks like a symbol is a symbolic
> element (this is probably obvious to everyone but me): in e.g. a
> polynomial algebra the generator X is a definite entity and *not* a free
> symbol. Every statement that makes sense in this algebra is known about
> X. A symbolic element would be a parametric family of polynomials,
> something like X**2 + p*X + q. Here the p and q are symbols that
> represent elements of the coefficient ring. It is sensible to make
> assumptions about them, e.g. p > 0 or "X**2 + p*X + q is irreducible" or
> whatever.

Interesting (and with series the same I suppose), I suppose that this
class of assumption related rather with first class/type of assumptions,
Or should it be separated to other class/type in the classification of
assumptions?

P.S.
I can read answers after May 10 only.


--
Alexey U.

Tom Bachmann

unread,
May 7, 2011, 12:28:28 PM5/7/11
to sy...@googlegroups.com
On 06.05.2011 17:32, Ronan Lamy wrote:
>> Notice that every element that we ever compute with belongs to some
>> algebra.
>
> Here's our fundamental disagreement, I think. Objects aren't
> intrinsically part of an algebra, they happen to belong to some set over
> which some algebraic structure is defined and used.
>

My point is that without being part of an "algebra" (or whatever we call
it), what does an operation on objects even *mean*, let alone how the
result is represented?

Yes in principle objects are part of sets on which algebraic structures
are (or can be) defined, but for a computer program it is not really
relevant what objects "are", what is relevant is how they are
represented and manipulated. What manipulations are possible is
essentially what I described as "to what algebra does this belong". And
in particular the representation should be chosen in such a way that the
desired manipulations can be done in an efficient fashion.


We are drifting away from assumptions here, but I would argue that in
sympy currently there are about three algebras. They are not being
properly identified as such, and they have no standardised way of
"talking to each other", which makes some things cumbersome. Here they are

o The "calculus" algebra (Add/Mul etc),
o the matrix algebra,
o the polynomial algebras.

Right now the first is meant to encompass everything, but a) it even
doesn't now, b) in the long-term this seems even more unlikely and c) in
any case an all-encompassing algebra can hardly be very efficient for
non-all-encompassing tasks.

Aaron S. Meurer

unread,
May 7, 2011, 2:11:13 PM5/7/11
to sy...@googlegroups.com
On May 5, 2011, at 1:00 PM, Haz wrote:



On Thu, May 5, 2011 at 12:29 PM, Aaron Meurer <asme...@gmail.com> wrote:
>   The big questions that remain are those involving (1): it is generally
> accepted that the old assumption system should give way to the new one, but
> to what extent we remove the old ones from the core? How the new ones
> replace the old ones? What is the syntax that we will use for them? What
> "assumptions" will we make about the assumption system? (i.e., local -vs-
> global context by default)

Well, one of the biggest problems with having the assumptions in the
core is that any time you want to edit the assumptions you have to
edit the core.  Having them separate should make them much more
extensible.

  It's worth picking apart that sentiment. When I say "to what extent we remove the old ones from the core?", I am considering the fact that is_positive may still remain as a function of Expr. The fact that it internally calls an ask(...) of the new assumption system is besides the point.

I mean that for example the logic to compute sqrt(x**2) => abs(x) or x is all in the core (that's just Pow(Pow(x, 2), S(1)/2)).  If we want to add a new assumption that doesn't exist yet, we would have to modify all the core methods to handle it correctly.  But if the core is just calling Ask() everywhere, or even better if simplification is left to some handle function like refine(), then this isn't a problem.

Aaron Meurer

Aaron S. Meurer

unread,
May 7, 2011, 2:20:27 PM5/7/11
to sy...@googlegroups.com

I agree. Having global directives like this is a bad idea, if just because it's extremely annoying to test. To do it correctly, you would have to run the tests with it off and then again with it on, which causes a combinatorial explosion in the number of combinations we have to test. Of course, in reality, if the module is well tested on its own, you should only have to run the tests for that module (like in theory, you should only have to run the polys tests in the different ground types). But furthermore, if some test sets the global parameter (like was happening with keep_sign), you get different results depending on what order the tests are run in.

This is one of the reasons why I'm presently leaning toward some kind of local assumptions implementation, like Vinzent's.

Aaron Meurer

Aaron S. Meurer

unread,
May 7, 2011, 2:27:21 PM5/7/11
to sy...@googlegroups.com

On May 6, 2011, at 6:09 PM, Alexey U. Gudchenko wrote:

> 02.05.2011 20:25, Ronan Lamy пишет:
>> Le dimanche 01 mai 2011 à 23:37 -0700, Ondrej Certik a écrit :
>
>>>
>>> You should assume it. Non commutative symbols are just making things
>>> very complicated, and it was a mistake to add them into the core of
>>> sympy. They should start in sympy.quantum, and only after they are
>>> being used a lot, we should start thinking how to handle them
>>> properly.
>>
>> I agree on this. Non-commutative Symbols don't make a lot of sense. If
>> we didn't have them (and severed the inheritance from Boolean), we could
>> say that Symbols are complex variables. Instead, we have a murky
>> definition.
>> Anyway, is_commutative is just an extreme example. Most .__new__()
>> and .eval() methods check one assumption or other. We'd have to throw
>> all that code away and rewrite it from scratch.
>>
>
>
> Non-commutative Symbols are generalization, commutative Symbols are specialization. (Remarking that it is a pure mathematica, not a quantum - because there are no physical lows used when we consider Non-commutative Symbols),
> but I agree that temporary it can be placed in sympy.quantum for tactic aims.
>
> In any case it would be convinient (and for effective core - to avoid unnesesary checking) to isolate in the core the possibility to work with commutative Symbols and possibility to work with non-commutative Symbols (or even with non-associative symbols). Now, as I understand, in the SymPy core the non-commutative core is not isolated, and at the same time by defult the is_commutative is switch off (False) - but many unnecessary checking are used further and we continue to deal with commutative.

Basically, the Mul (in Mul.flatten()) keeps track of a commutative and non-commutative part of an expression. The problem with keeping it in the core is that every function has to properly handle non-commutatives. There are quite a few cases where functions make the assumption that the expression is entirely commutative and return wrong results as a result.

This also goes back to the extensibility/editability of the core. If you want to modify the core, and do it correctly, you need to make sure non-commutative expressions are handled correctly.

Aaron Meurer

Ronan Lamy

unread,
May 7, 2011, 4:21:41 PM5/7/11
to sy...@googlegroups.com
Le samedi 07 mai 2011 à 17:28 +0100, Tom Bachmann a écrit :
> On 06.05.2011 17:32, Ronan Lamy wrote:
> >> Notice that every element that we ever compute with belongs to some
> >> algebra.
> >
> > Here's our fundamental disagreement, I think. Objects aren't
> > intrinsically part of an algebra, they happen to belong to some set over
> > which some algebraic structure is defined and used.
> >
>
> My point is that without being part of an "algebra" (or whatever we call
> it), what does an operation on objects even *mean*, let alone how the
> result is represented?

And what does an "algebra" mean without its elements? People knew that 2
+ 2 = 4 before Dedekind told them.

Also, if I understand correctly what you're saying, you're going further
than Integer(1) != Rational(1), you're implying IntegerRing(1) !=
IntegerAdditiveGroup(1).

> Yes in principle objects are part of sets on which algebraic structures
> are (or can be) defined, but for a computer program it is not really
> relevant what objects "are", what is relevant is how they are
> represented and manipulated. What manipulations are possible is
> essentially what I described as "to what algebra does this belong". And
> in particular the representation should be chosen in such a way that the
> desired manipulations can be done in an efficient fashion.

What objects are is relevant to a computer. The concepts of integer and
floating point number and their operations are embedded in the physical
structure of every CPU.

Anyway, you're confusing the problem domain we're modelling
(mathematics) with the actual code that models it. In Python, the thing
that tells how objects are represented and manipulated is called a
class. Algebras have different properties from classes and are things
that should be modelled in sympy, so they have to be Python objects.

> We are drifting away from assumptions here, but I would argue that in
> sympy currently there are about three algebras. They are not being
> properly identified as such, and they have no standardised way of
> "talking to each other", which makes some things cumbersome. Here they are
>
> o The "calculus" algebra (Add/Mul etc),
> o the matrix algebra,
> o the polynomial algebras.

I'm not sure how your vision of algebras relates to the current code,
but there are more kinds of objects than that. You're forgetting sets,
booleans, the various kinds of quantum objects, ... The "calculus
algebra" is basically Expr and all its subclasses.

It is loading more messages.
0 new messages