How to define a symbolic function which involves another function

2,086 views
Skip to first unread message

Shriramana Sharma

unread,
Nov 17, 2012, 11:14:56 PM11/17/12
to sy...@googlegroups.com
Hello. I have the function binomCoeff defined (from Wikipedia) as:

def binomCoeff ( n, k ) :
if k < 0 or k > n : return 0
if k > n - k : k = n - k # take advantage of symmetry
c = 1
for i in range ( 1, k + 1 ) : # 1 to k
c = c * ( n - ( k - i ) )
c = c // i
return c

but when I try to use it as part of the definition of a symbolic
function, I get a TypeError:

from sympy import *
var('lamb mu')
l, i = symbols('l i',integer=True)
f=symbols('f',cls=Function)
f = lamb * ( 1 + (-mu)**(l+i) ) * binomCoeff ( l + i, i )
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
<ipython-input-6-da625a708ef7> in <module>()
----> 1 f = lamb * ( 1 + (-mu)**(l+i) ) * binomCoeff ( l + i, i )

<ipython-input-2-f9467fac1a48> in binomCoeff(n, k)
3 if k > n - k : k = n - k # take advantage of symmetry
4 c = 1
----> 5 for i in range ( 1, k + 1 ) : # 1 to k
6 c = c * ( n - ( k - i ) )
7 c = c // i

TypeError: 'Add' object cannot be interpreted as an integer

Obviously it is trying to evaluate the function binomCoeff at the time
of defining of the function f, and I have to tell it to delay the
evaluation of binomCoeff because it is also a symbolic function, but
even if I do binomCoeff=symbols('binomCoeff',cls=Function) and then do
the def binomCoeff stuff, I still get the same error.

I would most appreciate it if anyone could advise me on what to do. Thanks!

--
Shriramana Sharma

Chris Smith

unread,
Nov 18, 2012, 2:59:26 AM11/18/12
to sy...@googlegroups.com

I would most appreciate it if anyone could advise me on what to do. Thanks!


Use SymPy ! :-)

>>> binomial(i,j)
binomial(i, j)
>>> _.subs(i,5)
binomial(5, j)
>>> _.subs(j,2)
10

So your expression is written as

>>> var('lamb mu')
(lamb, mu)
>>> l, i = symbols('l i',integer=True)
>>> f=symbols('f',cls=Function)
>>> f = lamb * ( 1 + (-mu)**(l+i) ) * binomial(l+i,i)
>>> pprint(f)
     ⎛    i + l    ⎞ ⎛i + l⎞
lamb⋅⎝(-μ)      + 1⎠⋅⎜     ⎟
                     ⎝  i  ⎠
>>> f.subs(((i,5),(l,2)))
21*lamb*(-mu**7 + 1)

Please note that although you are thinking of f as a function, it is (as you have defined it) actually an expression equal to what is on the right hand side. If you want f to be a function of l and i (for a fixed mu and lamb) then you might write

>>> f=lambda l,i:lamb * ( 1 + (-mu)**(l+i) ) * binomial(l+i,i)
>>> f(2, 5)
21*lamb*(-mu**7 + 1)

or write the function as

>>> def f(l, i):
...     return lamb * ( 1 + (-mu)**(l+i) ) * binomial(l+i,i)
...
>>> f(5, 2)
21*lamb*(-mu**7 + 1)
>>> f(a,b)
lamb*((-mu)**(a + b) + 1)*binomial(a + b, b)
>>> f(l,i)
lamb*((-mu)**(i + l) + 1)*binomial(i + l, i)

If you want to include an undefined function in an expression, that's the time to use f as a function:

>>> f = Function('f')
>>> f(1,2) + 3
f(1, 2) + 3


Hope that help,
Chris

Shriramana Sharma

unread,
Nov 18, 2012, 4:33:16 AM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 1:29 PM, Chris Smith <smi...@gmail.com> wrote:
>
>>>> def f(l, i):
> ... return lamb * ( 1 + (-mu)**(l+i) ) * binomial(l+i,i)
> ...

This is what I ended up figuring out myself! Anyhow, thanks for the
detailed explanation. I'll be back with more queries later. :-)

--
Shriramana Sharma

Shriramana Sharma

unread,
Nov 18, 2012, 9:46:04 AM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 3:03 PM, Shriramana Sharma <sam...@gmail.com> wrote:
> I'll be back with more queries later. :-)

OK so here's my next query on the same thread:

In [1]: from sympy import *
...: i,j=symbols('i j',integer=True)
...: lamb=list(symbols('lamb:4'))
...: n=3
...: summation(lamb[i]*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))
...:
---------------------------------------------------------------------------
TypeError Traceback (most recent call last)
/mnt/sda7/samjnaa-precise/<ipython-input-1-0af017dc9283> in <module>()
3 lamb=list(symbols('lamb:4'))
4 n=3
----> 5 summation(lamb[i]*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))
6

TypeError: list indices must be integers, not Symbol

OK so since lamb is a Python list and not a SymPy list (is there such
a thing?) Python tries to dereference it using i and throws the error.
Basically I want to do a summation of lamb0 ... lamb3 with the
appropriate coefficients. What to do?

Thanks!

--
Shriramana Sharma

Shriramana Sharma

unread,
Nov 18, 2012, 10:17:03 AM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 8:16 PM, Shriramana Sharma <sam...@gmail.com> wrote:
>
> ...: summation(lamb[i]*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))

One logical error I found in the above line is that j doesn't have a
definite (integer) value, so how can summation run i from n-j to n,
but even when I added at the head of the above statement:

for j in range(n+1):

... I still get the same error. Obviously, Python is still trying to
prematurely evaluate lamb[i]. I then tried to do an indirection by
defining:

def getlamb(i): return lamb[i]

... and replacing lamb[i] by getlamb(i), but still the same premature
evaluation happens. Please help!

--
Shriramana Sharma

Chris Smith

unread,
Nov 18, 2012, 10:24:04 AM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 9:02 PM, Shriramana Sharma <sam...@gmail.com> wrote:
On Sun, Nov 18, 2012 at 8:16 PM, Shriramana Sharma <sam...@gmail.com> wrote:
>
>    ...: summation(lamb[i]*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))


Something like this?

>>> var('i j n')
(i, j, n)
>>> lamb = Function('lamb') 
>>> Sum(lamb(i)*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))
Sum((-1)**(-j + n)*lamb(i)*binomial(i, -j + n), (i, -j + n, n))
>>> _.subs(((i,3),(j,4)))
Sum((-1)**(n - 4)*lamb(i)*binomial(i, n - 4), (i, n - 4, n))
>>> _.subs(n,6)
Sum(i*(i - 1)*lamb(i)/2, (i, 2, 6))
>>> _.doit()
lamb(2) + 3*lamb(3) + 6*lamb(4) + 10*lamb(5) + 15*lamb(6)
>>> 

Shriramana Sharma

unread,
Nov 18, 2012, 10:42:06 AM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 8:54 PM, Chris Smith <smi...@gmail.com> wrote:
>>>> lamb = Function('lamb')

Hey very nice, but I actually want to later on substitute values for
lamb0..3, and if lamb is an undefined function as the above, how do I
do that?

BTW as and when I use SymPy I am slowly coming to think that
successful use of SymPy involves use of both Python and SymPy
semantics, right? If I do the summation manually i.e. using Py
semantics, it's quite straightforward, but my query is how to do it
using SymPy's summation semantics.

Thanks ever more for your kind help! :-)

--
Shriramana Sharma

Chris Smith

unread,
Nov 18, 2012, 11:33:24 AM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 9:27 PM, Shriramana Sharma <sam...@gmail.com> wrote:
On Sun, Nov 18, 2012 at 8:54 PM, Chris Smith <smi...@gmail.com> wrote:
>>>> lamb = Function('lamb')

Hey very nice, but I actually want to later on substitute values for
lamb0..3, and if lamb is an undefined function as the above, how do I
do that?


>>> f=Function('f')
>>> f(1)+f(2)
f(1) + f(2)
>>> _.replace(f, lambda x: x**2)
5
>>> 

 
BTW as and when I use SymPy I am slowly coming to think that
successful use of SymPy involves use of both Python and SymPy
semantics, right? If I do the summation manually i.e. using Py
semantics, it's quite straightforward, but my query is how to do it
using SymPy's summation semantics.


I'm not sure what you mean. Using summation is ding it with sympy semantics. Perhaps you can post the way you think is un-sympy-ish. 

Shriramana Sharma

unread,
Nov 18, 2012, 1:09:19 PM11/18/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 10:03 PM, Chris Smith <smi...@gmail.com> wrote:
>>>> _.replace(f, lambda x: x**2)

No I mean: I have individual values for lamb0, lamb1, lamb2, lamb3. So
what I did was:

from sympy import *
var('i j n')
lamb=Function('lamb')
f=Sum(lamb(i)*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))
mylamb=list(var('lamb:4'))
nval=3
for jval in range(nval+1):
print(f.subs(((j,jval),(n,nval))).doit().replace(lamb,lambda
x:mylamb[x]))

which gave the "desired" output of:

-lamb3
lamb2 + 3*lamb3
-lamb1 - 2*lamb2 - 3*lamb3
lamb0 + lamb1 + lamb2 + lamb3

Obviously I'll have to have a proper mylamb list (containing floats)
for my application, but this is just experimentation...

But what's with so many ((( with the subs method?

> I'm not sure what you mean. Using summation is ding it with sympy semantics.
> Perhaps you can post the way you think is un-sympy-ish.

What I considered *unsympyish* is to do instead of summation(f,(i,a,b)):

s=0
for ival in range(a,b+1): s+=f.subs(i,ival)

BTW can you please clarify the difference between Sum and summation?
The doc says "represents unevaluated summation". So is it just that
until I say doit() it doesn't give me the output but remains a
symbolic function or such?

Another curious thing which I came across in the process of the above:

In [1]: from sympy import *
In [2]: lamb=list(var('lamb:4'))
In [3]: lamb
Out[3]: [lamb0, lamb1, lamb2, lamb3]
In [4]: lamb0=4
In [5]: lamb
Out[5]: [lamb0, lamb1, lamb2, lamb3]
In [6]: lamb[0]
Out[6]: lamb0
In [7]: type(lamb[0])
Out[7]: sympy.core.symbol.Symbol
In [8]: type(lamb0)
Out[8]: builtins.int

I don't get it -- there are *two* objects with name lamb0 in the
current namespace? How can that be?

Thanks as ever!

--
Shriramana Sharma

Chris Smith

unread,
Nov 18, 2012, 1:50:34 PM11/18/12
to sy...@googlegroups.com
No I mean: I have individual values for lamb0, lamb1, lamb2, lamb3. So
what I did was:

from sympy import *
var('i j n')
lamb=Function('lamb')
f=Sum(lamb(i)*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))
mylamb=list(var('lamb:4'))
nval=3
for jval in range(nval+1):
        print(f.subs(((j,jval),(n,nval))).doit().replace(lamb,lambda
x:mylamb[x]))

which gave the "desired" output of:

-lamb3
lamb2 + 3*lamb3
-lamb1 - 2*lamb2 - 3*lamb3
lamb0 + lamb1 + lamb2 + lamb3


>>> var('lamda')
lamda
>>> e = lambda n, j: Sum(Subs(lamda(x),x,i)*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n)).doit()

That Subs was created to delay substitution. It says "don't replace the function arg until you have a value for the replacement 'i'".

e is now a 2-argument function (defined as a 1-liner but could have been defined as

def e(n,j):
    return Sum(Subs(lamda(x),x,i)*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n)).doit()


Now let's see e in action:

>>> e(5,2)
-lamda(3) - 4*lamda(4) - 10*lamda(5)

Note: if the "doit" were not part of e's definition then e(5,2) would have given us

Sum(-i*(i - 2)*(i - 1)*Subs(lamda(x), (x,), (i,))/6, (i, 3, 5))

and then we would have to apply doit() to get it to evaluate.

Now, if you have specific values of lamda you can just substitute them in. Here I only replace lamda(3):

>>> _.subs(lamda(3), 2)
-4*lamda(4) - 10*lamda(5) - 2

Now here's your loop:

>>> n=3
>>> for j in range(n+1):
...  print e(n,j)
... 
-lamda(3)
lamda(2) + 3*lamda(3)
-lamda(1) - 2*lamda(2) - 3*lamda(3)
lamda(0) + lamda(1) + lamda(2) + lamda(3)

 Looks familiar, right?


But what's with so many ((( with the subs method?


foo.subs(old, new) or foo.subs( [ (old1, new1), (old2, new2) ] ); the 2nd case gives a list (or tuple) of replacements to apply sequentially, i.e. foo.subs(old1,new1).subs(old2,new2).
 
BTW can you please clarify the difference between Sum and summation?
The doc says "represents unevaluated summation". So is it just that
until I say doit() it doesn't give me the output but remains a
symbolic function or such?


summation tries to do the summation and if it can't it returns the Sum; if you enter it as a Sum it says "whether you can or not, don't evaluate it yet", i.e. summation *might* evaluate; Sum won't.
 
Another curious thing which I came across in the process of the above:

In [1]: from sympy import *
In [2]: lamb=list(var('lamb:4'))
In [3]: lamb
Out[3]: [lamb0, lamb1, lamb2, lamb3]
In [4]: lamb0=4
In [5]: lamb
Out[5]: [lamb0, lamb1, lamb2, lamb3]
In [6]: lamb[0]
Out[6]: lamb0
In [7]: type(lamb[0])
Out[7]: sympy.core.symbol.Symbol
In [8]: type(lamb0)
Out[8]: builtins.int

I don't get it -- there are *two* objects with name lamb0 in the
current namespace? How can that be?


no, one is lamb0 the other is list lamb; lamb[0] is item 0 of list lamb.

Ronan Lamy

unread,
Nov 18, 2012, 2:52:47 PM11/18/12
to sy...@googlegroups.com
Le 18/11/2012 18:09, Shriramana Sharma a �crit :
> On Sun, Nov 18, 2012 at 10:03 PM, Chris Smith <smi...@gmail.com> wrote:
>>>>> _.replace(f, lambda x: x**2)
>
> No I mean: I have individual values for lamb0, lamb1, lamb2, lamb3. So
> what I did was:
>
> from sympy import *
> var('i j n')
> lamb=Function('lamb')
> f=Sum(lamb(i)*(-1)**(n-j)*binomial(i,n-j),(i,n-j,n))
> mylamb=list(var('lamb:4'))
> nval=3
> for jval in range(nval+1):
> print(f.subs(((j,jval),(n,nval))).doit().replace(lamb,lambda
> x:mylamb[x]))
>
> which gave the "desired" output of:
>
> -lamb3
> lamb2 + 3*lamb3
> -lamb1 - 2*lamb2 - 3*lamb3
> lamb0 + lamb1 + lamb2 + lamb3
>
> Obviously I'll have to have a proper mylamb list (containing floats)
> for my application, but this is just experimentation...
>
> But what's with so many ((( with the subs method?

First, don't use var(), it'll just confuse you. The second line should
be "i, j, n = symbols('i j n')" and the fifth:
"mylamb = symbols('lamb:4')".

To do the substitutions, the simplest way in this case is to create a
substitution dictionary, and then call subs(), like this (assuming
Python 2.7+):
substs = {lamb(k): value for k, value in enumerate(mylamb)}
print f.subs({j: jval, n: nval}).doit().subs(substs)


>> I'm not sure what you mean. Using summation is ding it with sympy semantics.
>> Perhaps you can post the way you think is un-sympy-ish.
>
> What I considered *unsympyish* is to do instead of summation(f,(i,a,b)):
>
> s=0
> for ival in range(a,b+1): s+=f.subs(i,ival)
>
> BTW can you please clarify the difference between Sum and summation?
> The doc says "represents unevaluated summation". So is it just that
> until I say doit() it doesn't give me the output but remains a
> symbolic function or such?

Yes, that's it. Sum is a Python class that represents symbolic sums.
When you write Sum(...), you simply get an object of type 'Sum', with
attributes specified by the arguments of the call. summation(...) is
effectively just Sum(...).doit(). There are several (function, class)
pairs like this in sympy, another example is integrate/Integral.
>
> Another curious thing which I came across in the process of the above:
>
> In [1]: from sympy import *
> In [2]: lamb=list(var('lamb:4'))
> In [3]: lamb
> Out[3]: [lamb0, lamb1, lamb2, lamb3]
> In [4]: lamb0=4
> In [5]: lamb
> Out[5]: [lamb0, lamb1, lamb2, lamb3]
> In [6]: lamb[0]
> Out[6]: lamb0
> In [7]: type(lamb[0])
> Out[7]: sympy.core.symbol.Symbol
> In [8]: type(lamb0)
> Out[8]: builtins.int
>
> I don't get it -- there are *two* objects with name lamb0 in the
> current namespace? How can that be?

No there aren't. Don't mix up the python variable named 'lamb0' (which
is 4, per your assignment in In[4]) with the object Symbol('lamb0'),
which happens to be the value of lamb[0]. See
http://docs.sympy.org/dev/gotchas.html#variables-assignment-does-not-create-a-relation-between-expressions
for more explanations on this.
>
> Thanks as ever!
>

Shriramana Sharma

unread,
Dec 1, 2012, 4:07:17 AM12/1/12
to sy...@googlegroups.com
On Sun, Nov 18, 2012 at 10:03 PM, Chris Smith <smi...@gmail.com> wrote:
>> Hey very nice, but I actually want to later on substitute values for
>> lamb0..3, and if lamb is an undefined function as the above, how do I
>> do that?
>>
>
>>>> f=Function('f')
>>>> f(1)+f(2)
> f(1) + f(2)
>>>> _.replace(f, lambda x: x**2)

Hi a further thought on this previous reply of Chris':

I am sure all will agree that subscripted variables are often used in
mathematics. Hence my need for lamb(da)0...3 (...n). However, we are
unable to use a Python list for this although it can be easily
constructed using list(symbols('lamb:4')), because I can't include it
as part of a symbolic expression since anything like lamb[i] will
throw an index error. Chris' suggestion above is to use a function
lamb instead, and to replace it at the end using replace.

But if I want to create a series of simultaneous equations involving a
series of subscripted variables and give them to solve, the above
method doesn't work:

In [1]: from sympy import symbols,Function,solve
In [2]: x,y=symbols('x,y')
In [3]: A=Function('A')
In [4]: solve([x + 5*y - 2, -3*x + 6*y - 15])
Out[4]: {x: -3, y: 1}
In [5]: solve([A(0) + 5*A(1) - 2, -3*A(0) + 6*A(1) - 15])
Out[5]: []

whereas what I would expect with subscripted variables would be to get:
{A(0): -3, A(1): 1 }

What is the way out here? Or in other words, what is the SymPy way to
use subscripted variables? That is, I want x0 x1 etc and need to be
able to specify them by x and 0,1 etc. Of course, symbols('x'+str(i))
would work but it is a hack, and it wouldn't make it convenient to
construct expressions using such subscripted variables...

Thanks!

--
Shriramana Sharma

Chris Smith

unread,
Dec 1, 2012, 6:37:55 AM12/1/12
to sy...@googlegroups.com
>>> eqs=Tuple(*[A(0) + 5*A(1) - 2, -3*A(0) + 6*A(1) - 15])
>>> solve(eqs, eqs.atoms(Function))
{A(0): -3, A(1): 1}

Shriramana Sharma

unread,
Dec 1, 2012, 6:40:09 AM12/1/12
to sy...@googlegroups.com
Hi thanks for this! So does this mean that there is no need for
separate support for subscripted symbolic variables in SymPy?

--
Shriramana Sharma

Chris Smith

unread,
Dec 1, 2012, 7:02:41 AM12/1/12
to sy...@googlegroups.com
Hi thanks for this! So does this mean that there is no need for
separate support for subscripted symbolic variables in SymPy?


If the functions work, then no. But functions don't carry assumptions. Having you raise questions like this helps to flesh out situations where existing or new structures are needed.

Shriramana Sharma

unread,
Dec 1, 2012, 7:57:15 AM12/1/12
to sy...@googlegroups.com
On Sat, Dec 1, 2012 at 5:32 PM, Chris Smith <smi...@gmail.com> wrote:
> If the functions work, then no. But functions don't carry assumptions.

Can you clarify what you mean by "assumptions"?

And even though it seems (I haven't yet implemented that part of my
program yet) that it would be sufficient to use Functions the way you
have indicated, I still think it would be useful to have a symbolic
array kind of variable where each subscripted item i.e. val[0] val[1]
etc would be a distinct symbol. I am having very very vague ideas of
something like:

class SymList(Expr):
def __init__(self,name,size):
self.list=list(symbols(name+':'+str(size)))

etc etc... so that if lamb=SymList('lamb',5) then lamb[0] lamb[1] ...
lamb[4] are all valid expressions which I can use within integrate,
summation etc etc and I can even use lamb[i] where i is a symbol.
Something like summation(lamb[i],(i,0,4)) would then be valid -- I
hope I am making some kind of sense.

And I am not sure whether having it size-limited at initialization is
a good or bad idea, so well...

I admitted it, it is very vague, and I might be seriously off-base on
the syntax etc not being an expert programmer, but I hope you get what
kind of functionality I am expecting...

--
Shriramana Sharma

Chris Smith

unread,
Dec 1, 2012, 8:16:01 AM12/1/12
to sy...@googlegroups.com
On Sat, Dec 1, 2012 at 6:42 PM, Shriramana Sharma <sam...@gmail.com> wrote:
On Sat, Dec 1, 2012 at 5:32 PM, Chris Smith <smi...@gmail.com> wrote:
> If the functions work, then no. But functions don't carry assumptions.

Can you clarify what you mean by "assumptions"?

And even though it seems (I haven't yet implemented that part of my
program yet) that it would be sufficient to use Functions the way you
have indicated, I still think it would be useful to have a symbolic
array kind of variable where each subscripted item i.e. val[0] val[1]
etc would be a distinct symbol. I am having very very vague ideas of
something like:

class SymList(Expr):
    def __init__(self,name,size):
        self.list=list(symbols(name+':'+str(size)))

etc etc... so that if lamb=SymList('lamb',5) then lamb[0] lamb[1] ...

that's what symbols does for you; just store the result in an array if you want:

>>> a=symbols('a:11')
>>> a[1]
a1
>>> a
(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)

Have you looked at Indexed?

 

Shriramana Sharma

unread,
Dec 1, 2012, 8:30:54 AM12/1/12
to sy...@googlegroups.com
On Sat, Dec 1, 2012 at 6:46 PM, Chris Smith <smi...@gmail.com> wrote:
>
> that's what symbols does for you; just store the result in an array if you
> want:
>
>>>> a=symbols('a:11')
>>>> a[1]
> a1
>>>> a
> (a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)

But I already said that I can't use a Python list because I can't then
use something like a[i] as part of an expression where i is a symbol.
For example summation(a[i],(i,0,n)) would throw an IndexError or
something saying that the index of a list has to be an integer. The
SymList or something that I visualize should be able to handle the
above.

> Have you looked at Indexed?

Hey nice, but still I have this problem:

from sympy import IndexedBase
A=IndexedBase('A')
solve([A[0] + 5*A[1] - 2, -3*A[0]+ 6*A[1] - 15])

produces at the end of the source trace:

AttributeError: 'int' object has no attribute 'free_symbols'

The subscriptable symbol which I expect should be able to handle the
above solve command. Or if I have to alter my usage slightly (i.e.
without using all that atoms etc) for IndexedBase itself (or some
existing subclass thereof) to be able to handle the above solve,
please indicate it. Thanks!

--
Shriramana Sharma

Matthew Rocklin

unread,
Dec 1, 2012, 8:43:35 AM12/1/12
to sy...@googlegroups.com
I ran into this problem in MatrixExprs.  We don't currently have a subscripted symbol.  It would be nice though.  

In the following example
In [1]: X = MatrixSymbol('X', n, n)

In [2]: X[1, 2]
Out[2]: X₁₂

Ideally X[1, 2] is a Symbol that contains separate information for its origin X and its indices, 1, and 2.  Alas, all it knows is the combined string "X_12". 





--
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.


Rishabh Dixit

unread,
Dec 1, 2012, 9:52:01 AM12/1/12
to sy...@googlegroups.com
Hi,

I tried python lists and every thing seems fine to me-

>>> ls=symbols('a:10')
>>> ls
(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9)

Now I call a function Sum (it sums up a series in particular, infinite series)-

>>> for i in range(10):
...  Sum(1/(ls[i]**ls[i]),(ls[i],1,oo)).evalf()
... 
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266
1.29128599706266

Also,

>>> solve([ls[0]+5*ls[1]-2,-3*ls[0]+6*ls[1]-15])
{a0: -3, a1: 1}


It might be that I have misunderstood your problem :(


--
Regards,
Rishabh Dixit
BITS Pilani

Shriramana Sharma

unread,
Dec 1, 2012, 10:53:26 AM12/1/12
to sy...@googlegroups.com
On Sat, Dec 1, 2012 at 8:22 PM, Rishabh Dixit <rishabh...@gmail.com> wrote:
>>>> for i in range(10):
> ... Sum(1/(ls[i]**ls[i]),(ls[i],1,oo)).evalf()
> ...
>
>>>> solve([ls[0]+5*ls[1]-2,-3*ls[0]+6*ls[1]-15])
> {a0: -3, a1: 1}

Rishabh, basically the difference between what you are saying and I am
saying is, in the first example above you are still using i as a name
for Python builtin integer above and not a symbolic object and in the
second example, you're not using any symbolic i at all. Python lists
can take integer indices just fine. They can't take symbolic indices.
Try:

avals=list(symbols('a:10'))
i=symbols('i',integer=True)
print(summation(avals[i],(i,0,9)))

and you will see what I am talking about.

--
Shriramana Sharma

Aaron Meurer

unread,
Dec 1, 2012, 6:22:02 PM12/1/12
to sy...@googlegroups.com
On Dec 1, 2012, at 6:31 AM, Shriramana Sharma <sam...@gmail.com> wrote:

On Sat, Dec 1, 2012 at 6:46 PM, Chris Smith <smi...@gmail.com> wrote:

that's what symbols does for you; just store the result in an array if you
want:

a=symbols('a:11')
a[1]
a1
a
(a0, a1, a2, a3, a4, a5, a6, a7, a8, a9, a10)

But I already said that I can't use a Python list because I can't then
use something like a[i] as part of an expression where i is a symbol.
For example summation(a[i],(i,0,n)) would throw an IndexError or
something saying that the index of a list has to be an integer. The
SymList or something that I visualize should be able to handle the
above.

Have you looked at Indexed?

Hey nice, but still I have this problem:

from sympy import IndexedBase
A=IndexedBase('A')
solve([A[0] + 5*A[1] - 2, -3*A[0]+ 6*A[1] - 15])

produces at the end of the source trace:

AttributeError: 'int' object has no attribute 'free_symbols'

That is a bug that I believe was fixed in the git master recently. The workaround is to use A[S(1)] instead of A[1].

The bigger issue with Indexed is that it is noncommutative. See http://code.google.com/p/sympy/issues/detail?id=2659. I think this is the right class for you, but it is in need of some cleaning up. 

Aaron Meurer



The subscriptable symbol which I expect should be able to handle the
above solve command. Or if I have to alter my usage slightly (i.e.
without using all that atoms etc) for IndexedBase itself (or some
existing subclass thereof) to be able to handle the above solve,
please indicate it. Thanks!

--
Shriramana Sharma

Chris Smith

unread,
Dec 1, 2012, 8:29:40 PM12/1/12
to sy...@googlegroups.com
If you work with the current master you won't get the Indexed error:

>>> from sympy import IndexedBase
>>> A=IndexedBase('A')
>>> solve([A[0] + 5*A[1] - 2, -3*A[0]+ 6*A[1] - 15])
[]

But no solution...so let's try the Tuple-atoms trick:

>>> eqs=Tuple(*([A[0] + 5*A[1] - 2, -3*A[0]+ 6*A[1] - 15]))
>>> solve(eqs,eqs.atoms(IndexedBase))
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sympy/solvers/solvers.py", line 689, in solve
    raise TypeError(msg % type(s))
TypeError: expected Symbol, Function, Power or Derivative but got <class 'sympy.tensor.indexed.IndexedBase'>

(that should be fixed -- solve should be able to solve for other things, too)

So replace them with dummies:

>>> reps = [(i, Dummy()) for i in eqs.atoms(Indexed)]
>>> solve(eqs.subs(reps),[d for i, d in reps])
{_20: -3, _21: 1}

Make the solution a SymPy object capable of doing replacement:

>>> Dict(_).xreplace(dict([(v,k) for k,v in reps]))
{A[0]: -3, A[1]: 1}

And there we are!

Shriramana Sharma

unread,
Dec 1, 2012, 9:13:24 PM12/1/12
to sy...@googlegroups.com
On Sun, Dec 2, 2012 at 6:59 AM, Chris Smith <smi...@gmail.com> wrote:
> If you work with the current master you won't get the Indexed error:
>
>>>> from sympy import IndexedBase
>>>> A=IndexedBase('A')
>>>> solve([A[0] + 5*A[1] - 2, -3*A[0]+ 6*A[1] - 15])
> []
>
> But no solution...so let's try the Tuple-atoms trick:

Hi, the atoms trick will work whether I use IndexedBase or Function,
but the point is to have an IndexedBase such that A[i] for any
symbolic or integer i will represent a distinct symbolic object, so
that it can be used with solve(), right? So this doesn't really seem
to be a solution...

--
Shriramana Sharma

Shriramana Sharma

unread,
Dec 1, 2012, 10:21:20 PM12/1/12
to sy...@googlegroups.com
On Sat, Dec 1, 2012 at 7:13 PM, Matthew Rocklin <mroc...@gmail.com> wrote:
> In the following example
> In [1]: X = MatrixSymbol('X', n, n)
>
> In [2]: X[1, 2]
> Out[2]: X₁₂
>
> Ideally X[1, 2] is a Symbol that contains separate information for its
> origin X and its indices, 1, and 2. Alas, all it knows is the combined
> string "X_12".

Wouldn't it be advisable to separate the row and col indices in the
subscript: X₁,₂ ? Otherwise how to go beyond 9?

--
Shriramana Sharma

Chris Smith

unread,
Dec 1, 2012, 11:16:40 PM12/1/12
to sy...@googlegroups.com
I don't follow you...if the function or Indexed has a different name then it's different from others, and if the argument(s) of any function or Indexed are different they represent different objects.

>>> eqs=Tuple(f(1,2)+f(i)-3,f(1,2)-f(i)-4)
>>> solve(eqs, eqs.atoms(Function))
{f(i): -1/2, f(1, 2): 7/2}
>>> eqs=Tuple(g(j)+f(i)-3,g(j)-f(i)-4)
>>> solve(eqs, eqs.atoms(Function))
{f(i): -1/2, g(j): 7/2}

 

Chris Smith

unread,
Dec 2, 2012, 5:44:45 AM12/2/12
to sy...@googlegroups.com
What about this?

class Syms(object):
    """Return an Indexed object with name given at instantiation.
    Indexing can be done using matrix or function notation.

    Examples
    ========

    >> from sympy.future import Syms
    >> a=Syms('a')
    >> a[1]
    a[1]
    >> a(1)
    a[1]
    >> a(i,k)
    a[i, k]

    """
    def __new__(cls, name):
        obj = object.__new__(cls)
        obj.name = name
        return obj
    def __getitem__(self, *i):
        return Indexed(self.name, *i)
    def __call__(self, *i):
        return Indexed(self.name, *i)

>>> eqs=Tuple(a[i]+a[j]-3,a[i]-a[j]+4)
>>> idx=eqs.atoms(Indexed)
>>> dum=[Dummy() for ii in idx]  # don't use i as loop variable
>>> Dict(solve(eqs.subs(zip(idx, dum)), dum)).subs(zip(dum,idx))
{a[i]: -1/2, a[j]: 7/2}
>>> Sum(a[i],(i,1,4))
Sum(a[i], (i, 1, 4))
>>> _.doit()
a[1] + a[2] + a[3] + a[4]

Shriramana Sharma

unread,
Dec 2, 2012, 10:28:06 AM12/2/12
to sy...@googlegroups.com
On Sun, Dec 2, 2012 at 9:46 AM, Chris Smith <smi...@gmail.com> wrote:
> I don't follow you...if the function or Indexed has a different name then
> it's different from others, and if the argument(s) of any function or
> Indexed are different they represent different objects.
>
>>>> eqs=Tuple(f(1,2)+f(i)-3,f(1,2)-f(i)-4)
>>>> solve(eqs, eqs.atoms(Function))
> {f(i): -1/2, f(1, 2): 7/2}

But as I said in my previous mail, I would expect a subscripted symbol
(which IndexedBase intends to be) to be treated as a first-classs
symbolic object equal to i,j etc without resorting to any additional
effort like atoms, dummies, zip etc.

On Sun, Dec 2, 2012 at 4:14 PM, Chris Smith <smi...@gmail.com> wrote:
> What about this?
> class Syms(object):
> def __new__(cls, name):
> obj = object.__new__(cls)
> obj.name = name
> return obj
> def __getitem__(self, *i):
> return Indexed(self.name, *i)
> def __call__(self, *i):
> return Indexed(self.name, *i)
>
>>>> eqs=Tuple(a[i]+a[j]-3,a[i]-a[j]+4)
>>>> idx=eqs.atoms(Indexed)
>>>> dum=[Dummy() for ii in idx] # don't use i as loop variable
>>>> Dict(solve(eqs.subs(zip(idx, dum)), dum)).subs(zip(dum,idx))
> {a[i]: -1/2, a[j]: 7/2}
>>>> Sum(a[i],(i,1,4))
> Sum(a[i], (i, 1, 4))
>>>> _.doit()
> a[1] + a[2] + a[3] + a[4]

But the above still needs all that atoms and dummies and subs and zip
stuff stuff! :-( Too complicated for what should be elementary usage.
And I did myself try your suggeested implementation, but observe my
comments inlined:

from sympy import Indexed
class SymList ( object ) :
def __new__ ( cls, name ) :
obj = object . __new__ ( cls )
obj . name = name
return obj
def __getitem__ ( self, * i ) :
return Indexed ( self . name, * i )
def __call__ ( self, * i ) :
return Indexed ( self . name, * i )

Sorry if I am mistaken but the above seems little more than a thin
wrapper around Indexed. What does it provide that Indexed does not?

a=SymList('a')
a[1]
Out[4]: a[1]
a(1)
Out[5]: a[1]

So far so good, but see:

from sympy import symbols
i,j=symbols('i,j')
a[i,j]
Out[8]: a[(i, j)]
a(i,j)
Out[9]: a[i, j]

This discrepancy is an infecility: if for single items you provide []
and () as equal (which is not necessary and IMHO [] would be enough)
then the same must be true for tuples, but a[i,j] reads *args as a
tuple of a tuple (of i and j), whereas a(i,j) reads *args as a tuple
of i and j.

from sympy import solve
solve([a[i]+a[j]-3,a[i]-a[j]+4])
Out[12]: []

:-( So still my desired behaviour doesn't happen...

Sorry for being persistent, but really I'd very much like to use some
like (hopefully IndexedBase) in this manner without resorting to
"tricks" like atoms, dummies etc etc. I hope it would not be a serious
problem to provide that in SymPy. Since subscripted (i.e. indexed)
variables (and in some case even super+sub-scripted or i.o.w.
multi-index variables) are widely used in mathematics I hope I would
not be the only person to be benefited.

--
Shriramana Sharma

Shriramana Sharma

unread,
Dec 2, 2012, 11:34:22 AM12/2/12
to sy...@googlegroups.com
On Sun, Dec 2, 2012 at 6:59 AM, Chris Smith <smi...@gmail.com> wrote:
>>>> eqs=Tuple(*([A[0] + 5*A[1] - 2, -3*A[0]+ 6*A[1] - 15]))

Hi can you clarify why you have used a * inside the Tuple constructor
in both the above cases and why the additional () around the []
containing the list in the second case? The Tuple documentation
doesn't seem to speak about either of these two. Thanks!

--
Shriramana Sharma

Shriramana Sharma

unread,
Dec 2, 2012, 11:40:24 AM12/2/12
to sy...@googlegroups.com
Sorry for the extra mail!

On Sun, Dec 2, 2012 at 10:04 PM, Shriramana Sharma <sam...@gmail.com> wrote:
> Hi can you clarify why you have used a * inside the Tuple constructor
> in both the above cases and why the additional () around the []
> containing the list in the second case? The Tuple documentation
> doesn't seem to speak about either of these two. Thanks!

I found out from the Python documentation that * unpacks lists, but my
question about the extra () in the second example still stands...

--
Shriramana Sharma

Aaron Meurer

unread,
Dec 2, 2012, 3:25:00 PM12/2/12
to sy...@googlegroups.com
Of you have a tuple literal, it's redundant. Tuple(*(1,2)) is the same
as Tuple(1,2). But if you have a variable, you have to use Tuple(*a).

Aaron Meurer

Chris Smith

unread,
Dec 2, 2012, 7:36:40 PM12/2/12
to sy...@googlegroups.com

I found out from the Python documentation that * unpacks lists, but my
question about the extra () in the second example still stands...


It unpacks tuples, too. 

Chris Smith

unread,
Dec 4, 2012, 9:15:26 PM12/4/12
to sy...@googlegroups.com
OK, in the current master the Indexed quantities will be detected automatically.
Reply all
Reply to author
Forward
0 new messages