returning symbol in a function

58 views
Skip to first unread message

Thomas Ligon

unread,
Nov 3, 2020, 11:41:56 AM11/3/20
to sympy
The following code is not working as I expected.
Why does Sym1 contain an underline (_{1})?
Why does return fail (unhandled  exception) in line 7?

Code:
import sympy
from sympy import symbols, latex
def defSym1():
    return Sym1
def defSym2():
    strSym = 'return Sym2'
    exec(strSym)
Sym1 = symbols('Sym1')
Sym2 = symbols('Sym2')
print(latex(defSym1()))
print(latex(defSym2()))
print('end testSym')

Output:
Sym_{1}
'return' outside function (<string>, line 1)




Aaron Meurer

unread,
Nov 3, 2020, 5:29:00 PM11/3/20
to sympy
I'm unclear what you're trying to achieve with the functions. exec()
takes a string of Python code and executes it. The entire string must
be valid Python code by itself, so exec('return x') fails because a
bare "return x" is not valid Python. "return" must be inside a
function definition to be valid. But even defSym1 doesn't do anything
useful beyond just returning Sym1, so there's no point to having it
instead of just "Sym1" directly.

The LaTeX version of Sym1 contains _ because SymPy automatically
assumes that symbol names ending in numbers are subscripted, so it
renders it as a Sym_1 in LaTeX. If you don't want the 1 to be
subscripted, you can use something like Symbol("{Sym1}").

Aaron Meurer
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To unsubscribe from this group and stop receiving emails from it, send an email to sympy+un...@googlegroups.com.
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/4a1df466-d3a3-4f7b-8a14-5e5a906f2662n%40googlegroups.com.

Thomas Ligon

unread,
Nov 4, 2020, 3:51:35 AM11/4/20
to sympy
Thanks!
The automatic subscript is just a minor irritation, and your response is helpful.
I have included a new test program that hopefully no longer oversimplifies the situation. Currently, I have code (2000 lines) for investigating a very complex series, and it runs to a maximum of 3 terms of the series, requiring index values up to 6. I showed the results to the mathematics professor who has mentored my work and he immediately says that it is valuable and I should publish a version that can go up to a very large number of terms. In order to do that, I need to produce a fully automated version with a variable number of terms and indices.
So, for variable j, I want to use a_j in a formula.
The expression exp on line 20 does that, where I use get_a(1) to represent aj for variable j, where j happens to be 1.
In line 5, the return is part of a function. In line 8, I expect return in exec ('return...') to be a part of a function.

maxIndex = 2
import sympy
from sympy import symbols, latex
def get_a1():
    return a1
def get_a2():
    strSym = 'return a2'
    exec(strSym)
def get_a (j):
    strSym = 'return a' + str(j)
    exec(strSym)
lam = symbols('\\lambda')
for ind in range(1,maxIndex+1):
    strSym = 'a' + str(ind) + ' = symbols(\'a_' + str(ind) + '\')'
    exec(strSym)
#a1 = symbols('a_1')
#a2 = symbols('a_2')
print(latex(get_a1()))
#print(latex(get_a2())) # causes 'return' outside function (<string>, line 1)
exp = 3*get_a(1) + 5*get_a(2)*lam**2 # causes 'return' outside function (<string>, line 1)
print(latex(exp))
print('end testSym')

Thomas Ligon

unread,
Nov 4, 2020, 4:23:55 AM11/4/20
to sympy
Now I see a solution: forget the function and create the expression exp using string manipulation and exec, something like
strExp = 'complex string'
strSym = 'exp = ' + strExp
exec(strSym)

Aaron Meurer

unread,
Nov 4, 2020, 3:46:03 PM11/4/20
to sympy
You can't use exec() to do a return statement for a function that is
not also part of the string. What you want is eval(), which evaluates
an expression and returns it, like

return eval(string)

However, you should consider just using sympify() or parse_expr() if
you are evaluating strings to SymPy expressions, as they are designed
for that and will give you things like automatically defining the
symbols.

Actually, for what you've shown, I don't see the need to use exec() or
eval() at all. You can just create the symbols from the strings, like
Symbol('a' + str(j)). Symbols are completely defined by name, so if
two symbols have the same name, they will be equal.

Aaron Meurer
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/00d00ac2-7b0c-4ac1-b81a-dcc9f7956572n%40googlegroups.com.

Thomas Ligon

unread,
Nov 6, 2020, 3:15:34 PM11/6/20
to sympy
Thanks again for the very valuable help. I think I am getting very close to a good solution, but I must still have a bug in there.
First, just a small piece of background: I am using some symbols like
Round1 = symbols('\\(1\\)')
Round1Bar = symbols('\\Bar{\\(1\\)}')
In other words, a "1" in parentheses and the same thing with a bar over it, plus some using square brackets. These were defined and used in two papers I am referring to, published in 1854 and 1896, both by famous mathematicians. One version of my code used something like E_1 instead, but this time I chose to stick to the old symbols, even though they are not typical today, since they could be confused with normal parentheses.
Now, I have working code that has a hard-coded limit on the number of symbols and I need to upgrade it so that it can handle an undetermined (perhaps large) number of symbols. When creating expressions, I am using functions like this one:
 def getRoundjBar (j):
    strJ = str(abs(j))
    if j < 0:
        strJ = 'm' + strJ
    strSym = 'Round' + strJ + 'Bar'
    return parse_expr(strSym)
and here is some test code showing it in action:
ret1 = getRoundjBar(1)
print(ret1)
print(latex(ret1))
I can see that it is important for me to understand the type of the variable and, fortunately, the "Locals" window in my debugger shows that ret1 contains the value Round1Bar and has type Symbol. Good, that makes almost everything work well, but
print(latex(ret1)) 
produces
\bar{Round1}
where it should produce
\\bar{\(1\)} 

Aaron Meurer

unread,
Nov 6, 2020, 4:25:54 PM11/6/20
to sympy
I think Round as a shortcut for putting parentheses around something
is not implemented in the LaTeX printer. I've never seen that before,
but if it is common, we can add it. Otherwise, if you need custom
LaTeX in your symbol names you should just use it directly in the
symbol name.

Aaron Meurer
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/24d5bbe0-8388-4564-ad2f-10fa6412c94bn%40googlegroups.com.

Thomas Ligon

unread,
Nov 6, 2020, 5:54:21 PM11/6/20
to sympy
Hi Aaron,
this remark really throws me off. I certainly didn't think that Round was a shortcut for anything. Here is how I understood Sympy:
When I write
Round1 = symbols('\\(1\\)') 
then I assume that the right-hand side is the form used by Sympy for printout, especially for print(latex(...)), and the left-hand side is just a name that I invent, meaning that Round is my way of remembering what it is, and the name should avoid special characters that I want in the printout, such as parentheses and minus signs, because they cause unwanted things to happen in an expression. By the way, that is why I have a Roundm2 = symbols('\\(-2\\)')
meaning that I have used "m" to indicate (to me) that this is minus 2, but avoiding "-" in a name.
Now, as a bit more background, (j) is an arithmetic expression involving j and m (a constant in this context), meaning that (j) is a shortcut for something that would fill a lot of space in a normal printout.
By the way, in the recent test code
print(latex(Round1))
print(latex(parse_expr('Round1')))
print(latex(Round1Bar))
print(latex(parse_expr('Round1Bar')))
produces
\(1\)
Round_{1}
\bar{\(1\)}
\bar{Round1}
so the problem I have is that
print(latex(symbolname))
and
print(latex(parse_expr('symbolname')))
are different. My old code with static limits uses the first case, and the new code with variable limits uses the second.
I can see now that parse_expr('string') is not converting 'string' into the name of a symbol, as I expected. I also see that suffix 1 is being converted to _{1} (as you mentioned earlier) and suffix Bar is being converted to prefix \bar. That explains where this form is coming from.
Finally, here is one more test to avoid the automatic assumption about suffixes (like Microsoft Word's autocorrect, but I won't go into that rant here).
Round2BarX = symbols('\\bar{\\(2\\)}')
print(latex(Round2BarX))
print(latex(parse_expr('Round2BarX')))
produces
\bar{\(2\)}
Round2BarX

Aaron Meurer

unread,
Nov 6, 2020, 7:47:14 PM11/6/20
to sympy
parse_expr converts any unknown variable names into symbols. If you
already have the variable Round1Bar defined as something you want it
to use, you should pass it in the locals dictionary, like

parse_expr('Round1Bar', {'Round1Bar', symbols(r'\(1\)')})

Or if you already have them defined you can just use

parse_expr('Round1Bar', locals())

Aaron Meurer
> To view this discussion on the web visit https://groups.google.com/d/msgid/sympy/2c2986a7-9d96-4dba-af62-b840c0c1736en%40googlegroups.com.

Thomas Ligon

unread,
Nov 7, 2020, 2:39:10 PM11/7/20
to sympy
Thanks! This is working perfectly, with the small exception that I need globals() instead of locals().

By the way, I had already seen this in the documentation but didn't understand how to use it. The Sympy documentation at
mentions local_dict and global_dict, but doesn't say much on how to use it (probably documented somewhere else, but I didn't do much searching for that).
This site gives 17 examples:
including a case that uses global_dict=global_dict, but that didn't work for me (it probably requires creating a dictionary called global_dict, but that is not part of the sample).

Reply all
Reply to author
Forward
0 new messages