pearu / sympycore

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

The 'with' statement, assumptions, delayed evaluation, etc #34

Open GoogleCodeExporter opened 9 years ago

GoogleCodeExporter commented 9 years ago
We clearly need a way to support nonstandard evaluation: for example, to
disable simplification entirely, to evaluate with assumptions, disable
commutativity, etc.

One option is to let all constructors support a special keyword argument
'using' (or some other name) with the following semantics:

def __new__(cls, *args, using=global_context):
    if using.simply_call_canonize_please:
        return cls.canonize(*args)
    else:
        # try to handle nonstandard evaluation
        ...

'global_context' would be a global object defined in the core, and the
default action would be to simply call canonize. Note that the default
action would involve checking only a single boolean flag, which would keep
overhead to a minimum. The user could change the action by passing in a
custom context object, for example an assumption to be considered:

    Sqrt(x**2, using=Assumptions(x > 0))

This is clearly annoying to do in repeated statements. But thanks to the
'with' statement introduced in Python 2.5, there's a possibility for
changing the global context in a less fragile way than with purely global
variables. The following statement would be equivalent to the previous:

    with Assumptions(x > 0):
        a = Sqrt(x**2)

The 'with' statement would inject the Assumptions object into
global_context and this action would temporarily disable the 
'simply_call_canonize_please' flag. When exiting the 'with' block,
everything would be cleaned up appropriately.

Another example, there could be a "Verbatim" context that disables evaluation:

    a = Add(2*x, Mul(1, x, using=Verbatim), using=Verbatim)

or, much simpler:

    with Verbatim:
        a = 2*x + 1*x

Another example would be a context in which the * operator preserves order:

    with Noncommutative:
        y = A*B - B*A

There could even be a protocol for injecting entirely user-defined
evaluation rules into standard operators, e.g. to perform the
simplification n!/(n-1)! -> n in Mul.

Original issue reported on code.google.com by fredrik....@gmail.com on 9 Dec 2007 at 6:10

GoogleCodeExporter commented 9 years ago
The `with` statement seems to be worth to be used, the ideas above
look reasonable.

A general note: expr constructors should have
minimal number of keywords and only such keywords that do not
affect the results properties, e.g. the equality tests and hash
values must not depend on the values of keyword arguments.
The `using` kw is ok in this respect.

Few notes:
- I would suggest `context` instead of `using` kw and the default
  value of global_context should be None. In fact, the kw itself should
  have None as default value and the body of a constructor should find
  the context from the parent namespace. For example:

def __new__(cls, *args, context=None):
    if context is None:
        context = get_object_by_name('__sympy_context__', global_context)
    if context.simply_call_canonize_please:
        return cls.canonize(*args)
    else:
        # try to handle nonstandard evaluation

The '__sympy_context__' variable would be set by the context manager
__enter__ method, for instance.

- The assumption contexts should support nesting, e.g.
    with Assumptions(x>0):
      with Assumptions(y>0):
        suite

  should work as

    with(Assumptions(And(x>0, y>0))):
      suite

  The 

    with Assumptions(x>1):
      with Assumptions(x>0):
        suite

  should work as

    with Assumptions(x>1):
      suite

  but this should be resolved in Assumptions(And(x>0,x>1)) anyway.

Original comment by pearu.peterson on 11 Dec 2007 at 10:48

GoogleCodeExporter commented 9 years ago
>  the default
>  value of global_context should be None. In fact, the kw itself should
>  have None as default value and the body of a constructor should find
>  the context from the parent namespace. For example:

I'm not sure what the benefit is of this. The external global_context
object is accessed by default either way, only with None, more work
is required to access it. If global_context is passed as a default
parameter, only an attribute lookup on a local variable is needed in
the regular case. The user can still define a custom default context
in his own code if desired.

- The assumption contexts should support nesting

Agreed.

Original comment by fredrik....@gmail.com on 11 Dec 2007 at 11:08

GoogleCodeExporter commented 9 years ago
Yes, this is might be a bit premature technical detail
that is related to the (unsaid) idea of avoiding the
kw argument from the first place. I am sure that we
can figure out a proper (simple and efficient) way to deal
with this when starting to implement the contexts.

Original comment by pearu.peterson on 11 Dec 2007 at 11:26