subs() and f(x=1, y=3) syntax

9 views
Skip to first unread message

Ondrej Certik

unread,
Nov 28, 2008, 9:06:37 AM11/28/08
to sy...@googlegroups.com
Hi,

there is a long discussion in the following two threads:

http://groups.google.com/group/sympy-patches/browse_thread/thread/83c33c0aa8560475/
http://groups.google.com/group/sympy-patches/browse_thread/thread/628ebc799ee57ec1

(don't forget to click next at the bottom of the page to get to the next pages).

First note, that our current .subs() is verstile enough to support
almost all kinds of syntax. For example:

In [1]: e = x**y+3*x+sin(y)

In [2]: e
Out[2]:
y
3⋅x + sin(y) + x

In [3]: e.subs(x, z)
Out[3]:
y
3⋅z + sin(y) + z

In [4]: e.subs({x: z})
Out[4]:
y
3⋅z + sin(y) + z

In [5]: e.subs(dict(x=z))
Out[5]:
y
3⋅z + sin(y) + z

In [6]: e.subs({x: 1, y: 2})
Out[6]: 4 + sin(2)

In [7]: e.subs(dict(x=1, y=2))
Out[7]: 4 + sin(2)


However, it may be useful to allow f(x=1, y=2) directly instead of the
equivalent e.subs(dict(x=1, y=2)).
There are pros and cons of each approach, so let me summarize it:

1) do not allow the e(x=1) syntax, i.e. raise an exception in e.__call__

this doesn't cause any problems

2) implement e.__call__ along these lines:

def __call__(self, *args, **kwargs):
return self.subs(kwargs)

so now e(x=1, y=2) will be possible. A huge disadvantage is that the
following code will fail:

x = Symbol("a")
f = x*y

f(x=3)

This is because .subs() will try to substitute for Symbol("x"), but
there is no Symbol("x") in the expression, only Symbol("a").

Another problem is that what if your Symbol contains characters that
cannot be in a Python variable, like "\", "-", ..., then it will not
work.

3) implement e.__call__ along these lines (nice trick by Riccardo):

def __call__(self, *args, **kwargs):
import inspect
frame = inspect.currentframe().f_back.f_globals
nkwargs = {}
try:
for k, v in kwargs.iteritems():
nkwargs[eval(k, frame)] = v
finally:
del frame
return self._subs_dict(nkwargs)

In [1]: t = Symbol("theta")

In [2]: f = x+t**2

In [3]: f(x=10,t=3)
Out[3]: 19

Now it correctly finds the right variable to substitute for. However,
I and Brian don't like this, because playing with frames is a messy
stuff.


Read also the threads for more opinions. For comparison, here is what Sage does:

sage: t = var("theta")
sage: t
theta
sage: f = x+t**2
sage: f(x=10,t=3)
theta^2 + 10


So Sage chose the option 2).

So which option should we choose?

Ondrej

Riccardo Gori

unread,
Nov 28, 2008, 10:09:28 AM11/28/08
to sy...@googlegroups.com

I think we should avoid option 2.
I prefer something like:

f({x:2, y:3})

it's 2 character longer but is more pythonic, and it works.

Using this we can call _subs_dict from __call__ without modifying Basic.subs.

Cheers,
Riccardo

> Ondrej


Vinzent Steinberg

unread,
Nov 30, 2008, 1:44:21 PM11/30/08
to sympy
I think e.subs(dict(x=1, y=2)) or e.subs({x:1, y:2}) is much better
than e.subs(x=1, y=2), because it's more explicit, pythonic and less
magic.

Vinzent

On Nov 28, 3:06 pm, "Ondrej Certik" <ond...@certik.cz> wrote:
> Hi,
>
> there is a long discussion in the following two threads:
>
> http://groups.google.com/group/sympy-patches/browse_thread/thread/83c...http://groups.google.com/group/sympy-patches/browse_thread/thread/628...

Neal Becker

unread,
Nov 30, 2008, 3:56:35 PM11/30/08
to sy...@googlegroups.com
On Sunday 30 November 2008, Vinzent Steinberg wrote:
> I think e.subs(dict(x=1, y=2)) or e.subs({x:1, y:2}) is much better
> than e.subs(x=1, y=2), because it's more explicit, pythonic and less
> magic.
>

Oh, I like the look of this (no idea about implementation).

Ondrej Certik

unread,
Nov 30, 2008, 4:50:07 PM11/30/08
to sy...@googlegroups.com

Just to make it clear --- do you like the look of this
e.subs(dict(x=1, y=2)) or this e.subs(x=1, y=2)?

Ondrej

Neal Becker

unread,
Nov 30, 2008, 5:43:50 PM11/30/08
to sy...@googlegroups.com

I like the look of the dictionary syntax, although I think e.subs({x:1, y:2})
looks better than e.subs(dict(x=1, y=2)).

llarsen

unread,
Dec 2, 2008, 1:08:13 PM12/2/08
to sympy
I am in favor of the syntax:

f({x:2,y:3}) and f(dict(x=2,y=2))

as opposed to

f(x=2,y=3)

I like the fact that it is simple and clean to implement, doesn't
require any magick, avoids the confusion of the f(x=2,y=3) syntax, and
still feels like a relatively natural syntax (to me). It doesn't quite
match the natural mathematical syntax, but I think it is close enough
to be readable by people who are new to sympy.

-Lance

Ondrej Certik

unread,
Dec 2, 2008, 3:27:57 PM12/2/08
to sy...@googlegroups.com

I also prefer the syntax:

f({x:2,y:3}) or the implied syntax f(dict(x=2,y=2))

as opposed to

f(x=2,y=3)

for exactly the reasons all of you stated above. Brian has expressed
his opinion here:

http://groups.google.com/group/sympy-patches/msg/fcedf3feed505e54
http://groups.google.com/group/sympy-patches/msg/6e23272b38c6249b

So let's implement it. I am sorry Lance it has taken so long to
discuss this, I think noone has realized all the possible problems
that arised.

Thanks,
Ondrej

Brian Granger

unread,
Dec 2, 2008, 4:26:44 PM12/2/08
to sy...@googlegroups.com
> I also prefer the syntax:
>
> f({x:2,y:3}) or the implied syntax f(dict(x=2,y=2))
>
> as opposed to
>
> f(x=2,y=3)

I am with you here, both the f(dict(x=2,y=2)) and f(x=2,y=3) are bad
as they treat x and y as strings, not symbols. Do we want to have
subs and __call__ look at the keys of the dics it gets and make sure
they are not strings? We could raise an exception and print a nice
warning like "you have used strings (x,y) in a dict, you probably
meant to use symbols, which will require the syntax: {x:3,y:4}"

Otherwise I worry that users will be tricked by this subtlety.

I still might want to revisit the .bindings idea later, but let's just
focus on this part of it right now.

> So let's implement it. I am sorry Lance it has taken so long to
> discuss this, I think noone has realized all the possible problems
> that arised.

Yes, this was way more subtle than I had realized initially.

Also, just to be clear, we are NOT going to allow f(1,2) for now, correct?

Brian

> Thanks,
> Ondrej
>
> >
>

llarsen

unread,
Dec 2, 2008, 4:45:52 PM12/2/08
to sympy
> So let's implement it. I am sorry Lance it has taken so long to
> discuss this, I think noone has realized all the possible problems
> that arised.

It has been nice to have feedback on this issue. Some of the problems
with the initial proposal did not occur to me, and it is nice to have
others who catch important nuances. It took a little time to hash
things out on the mailing lists, but I think what has been proposed
here is better overall so I guess it was worth it. I will fix the
patch and resubmit it in the next few days.

-Lance

Riccardo Gori

unread,
Dec 2, 2008, 5:00:09 PM12/2/08
to sy...@googlegroups.com

Thanks a lot Lance!

Riccardo

Reply all
Reply to author
Forward
0 new messages