This time a question: is there a preferred way of getting a complete
list of symbols reserved by SymPy, i.e. atoms which have a default
meaning (E, pi, EulerGamma, oo, etc.)?
By experimenting and reading the source, I found out that in the old
0.6.x this can be done with
import sympy as sy
L = sy.Basic.singleton.keys()
and in the more recent 0.7.x with
import re
import sympy as sy
L = filter( lambda name: re.match("__", name) is None, dir(sy.S) )
L = map( lambda name: str(eval("sy.S.%s" % name)), L )
The reason I'm asking is of course that the internals may change in
later versions, so an API-stable way to do this would be preferable.
The (current) use case is custom validation for user input that is going
to be sympified. It is foreseeable this could also be used in GUI
applications to build a list of default constants.
My aim here is to avoid unpleasant surprises in user-defined quantities
in the FEM project; e.g. if the user happened to pick the symbol E for
(say) a Young modulus, SymPy would interpret that as exp(1) in any
expression that was sympified. This in turn could confuse the expression
processing. Currently I use the above code to fetch the reserved names,
but is there a better way to get them?
And finally, a related question: is there a way to get a human-readable
description for a reserved name (e.g. "zoo" -> "complex infinity")? I
didn't find anything in the API for this.
On Wed, Aug 29, 2012 at 2:27 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
> Hi all,
> This time a question: is there a preferred way of getting a complete
> list of symbols reserved by SymPy, i.e. atoms which have a default
> meaning (E, pi, EulerGamma, oo, etc.)?
These are the singletoms (below, ignore methods with `__`). But they
can all be overwritten. The only reserved symbols that don't become
symbols upon sympification are Q-COSINE (the C will actually parse
like a symbol so in that sense it's not reserved; the "-" could help
you remember that).
> And finally, a related question: is there a way to get a human-readable
> description for a reserved name (e.g. "zoo" -> "complex infinity")? I
> didn't find anything in the API for this.
> On Wed, Aug 29, 2012 at 2:27 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>> Hi all,
>> This time a question: is there a preferred way of getting a complete
>> list of symbols reserved by SymPy, i.e. atoms which have a default
>> meaning (E, pi, EulerGamma, oo, etc.)?
> These are the singletoms (below, ignore methods with `__`). But they
> can all be overwritten. The only reserved symbols that don't become
> symbols upon sympification are Q-COSINE (the C will actually parse
> like a symbol so in that sense it's not reserved; the "-" could help
> you remember that).
So, dir(S) is the preferred way of getting a complete list :)
It also works in both old and new versions, so that's good. I think the
only remaining question is if this is likely to stay working for the
foreseeable future?
(The point being, at least my mind would be more at ease if there was a
dedicated API method.)
Proceeding from this point, it seems that in order to get the
corresponding strings that sympify() converts into these objects, the
additional map(...) step is needed. What I mean is:
import re
import sympy as sy
L = dir(sy.S)
objnames = filter( lambda name: re.match("__", name) is None, L )
strs = map( lambda name: str(eval("sy.S.%s" % name)), objnames )
print objnames
print strs
For example, running this on 0.6.7 (sorry for the old version; running
on Debian Stable at the moment) we get
with "strs" being what I was after. (In real-world use, I would further
filter out anything that matches is_Number, in order to drop the obvious
0, -1, 1.)
This further conversion is important because:
import sympy as sy
pi = sy.sympify("pi")
print type(pi) # => sympy.core.numbers.Pi
# but:
pi = sy.sympify("Pi")
print type(pi) # => sympy.core.symbol.Symbol (<--- !!!)
...so the "reserved symbol" (in the sense I intended) should be
lowercase "pi".
>> And finally, a related question: is there a way to get a human-readable
>> description for a reserved name (e.g. "zoo" -> "complex infinity")? I
>> didn't find anything in the API for this.
> Not sure how to go further than
descs = {}
for obj,name in zip(objs,strs):
descs[name] = str(type(obj))
# now:
# - strs[] contains all reserved atoms, except literal numbers
# - descs{} contains a human-readable description of each item.
---8<---8<---8<---
I would like to suggest adding an API method for this (e.g. with the
is_Number check made optional), unless my use case is too specific for
general use :)
descs = {}
for obj,name in zip(objs,strs):
descs[name] = str(type(obj))
# Handle the set "Q-COSINE".
#
# C parses to Symbol, so strictly speaking it is not reserved
# in the sense meant here. Hence, we leave it out.
#
for s in "QOSINE":
obj = sy.sympify(s)
if obj not in objs: # I and E are likely to end up in twice, so
check first
objs.append( obj )
strs.append( s )
descs[s] = str(obj) # for most of these this is ok
descs["S"] = "the SymPy singleton system" # str(obj) == "S", not
helpful
descs["N"] = "the SymPy numerical evaluator" # str(obj) ==
"<function N at...>"
# Now:
# - strs[] contains names of all reserved symbols
# (except literal numbers if ignore_numbers is True)
# - descs{} contains a human-readable description of each item,
# keyed by the items in strs[].
#
return (strs,descs)
---8<---8<---8<---
This still leaves open the possibility of the user overwriting the
default singleton atoms, but I'm not sure how to handle that.
That depends on what is meant by overwriting - if the updated symbol is
stored into the singleton system, overwriting the old one, then this
approach already takes that into account. The whole idea here is to
dynamically query the state of the singleton system at that point of
program execution where the list is needed.
But of course stuff like
pi = sy.sympify("3")
expr = sy.sin(2*pi) # actually, sin(6)
(which does not involve sympification for expr) is outside the scope of
this solution.
> On 29/08/12 12:51, Chris Smith wrote:
>> On Wed, Aug 29, 2012 at 2:27 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>>> Hi all,
>>> This time a question: is there a preferred way of getting a complete
>>> list of symbols reserved by SymPy, i.e. atoms which have a default
>>> meaning (E, pi, EulerGamma, oo, etc.)?
>> These are the singletoms (below, ignore methods with `__`). But they
>> can all be overwritten. The only reserved symbols that don't become
>> symbols upon sympification are Q-COSINE (the C will actually parse
>> like a symbol so in that sense it's not reserved; the "-" could help
>> you remember that).
> Thanks for the quick reply.
> So, dir(S) is the preferred way of getting a complete list :)
> It also works in both old and new versions, so that's good. I think the
> only remaining question is if this is likely to stay working for the
> foreseeable future?
> (The point being, at least my mind would be more at ease if there was a
> dedicated API method.)
> Proceeding from this point, it seems that in order to get the
> corresponding strings that sympify() converts into these objects, the
> additional map(...) step is needed. What I mean is:
> import re
> import sympy as sy
> L = dir(sy.S)
> objnames = filter( lambda name: re.match("__", name) is None, L )
> strs = map( lambda name: str(eval("sy.S.%s" % name)), objnames )
> print objnames
> print strs
> For example, running this on 0.6.7 (sorry for the old version; running
> on Debian Stable at the moment) we get
> with "strs" being what I was after. (In real-world use, I would further
> filter out anything that matches is_Number, in order to drop the obvious
> 0, -1, 1.)
> This further conversion is important because:
> import sympy as sy
> pi = sy.sympify("pi")
> print type(pi) # => sympy.core.numbers.Pi
> # but:
> pi = sy.sympify("Pi")
> print type(pi) # => sympy.core.symbol.Symbol (<--- !!!)
> ...so the "reserved symbol" (in the sense I intended) should be
> lowercase "pi".
>>> And finally, a related question: is there a way to get a human-readable
>>> description for a reserved name (e.g. "zoo" -> "complex infinity")? I
>>> didn't find anything in the API for this.
>> Not sure how to go further than
>>>>> type(oo)
>> <class 'sympy.core.numbers.Infinity'>
> Aaaahh, the type! Nice.
> Thanks!
> The final code that I ended up using (modulo variable naming and
> comments) is:
> descs = {}
> for obj,name in zip(objs,strs):
> descs[name] = str(type(obj))
> # now:
> # - strs[] contains all reserved atoms, except literal numbers
> # - descs{} contains a human-readable description of each item.
> ---8<---8<---8<---
> I would like to suggest adding an API method for this (e.g. with the
> is_Number check made optional), unless my use case is too specific for
> general use :)
> On 29/08/12 12:51, Chris Smith wrote:
>> On Wed, Aug 29, 2012 at 2:27 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>>> Hi all,
>>> This time a question: is there a preferred way of getting a complete
>>> list of symbols reserved by SymPy, i.e. atoms which have a default
>>> meaning (E, pi, EulerGamma, oo, etc.)?
>> These are the singletoms (below, ignore methods with `__`). But they
>> can all be overwritten. The only reserved symbols that don't become
>> symbols upon sympification are Q-COSINE (the C will actually parse
>> like a symbol so in that sense it's not reserved; the "-" could help
>> you remember that).
> Thanks for the quick reply.
> So, dir(S) is the preferred way of getting a complete list :)
> It also works in both old and new versions, so that's good. I think the
> only remaining question is if this is likely to stay working for the
> foreseeable future?
Actually, if you use sympify(), almost everything in the sympy namespace is handled specially ('C' is probably the only significant exception, for obscure reasons), so IIUC the list you want is dir(sympy). The way parsing works now is hackish and probably unsafe, so don't count on it remaining the same.
> (The point being, at least my mind would be more at ease if there was a
> dedicated API method.)
> Proceeding from this point, it seems that in order to get the
> corresponding strings that sympify() converts into these objects, the
> additional map(...) step is needed. What I mean is:
> import re
> import sympy as sy
> L = dir(sy.S)
> objnames = filter( lambda name: re.match("__", name) is None, L )
> strs = map( lambda name: str(eval("sy.S.%s" % name)), objnames )
> print objnames
> print strs
> For example, running this on 0.6.7 (sorry for the old version; running
> on Debian Stable at the moment) we get
> with "strs" being what I was after. (In real-world use, I would further
> filter out anything that matches is_Number, in order to drop the obvious
> 0, -1, 1.)
> This further conversion is important because:
> import sympy as sy
> pi = sy.sympify("pi")
> print type(pi) # => sympy.core.numbers.Pi
> # but:
> pi = sy.sympify("Pi")
> print type(pi) # => sympy.core.symbol.Symbol (<--- !!!)
> ...so the "reserved symbol" (in the sense I intended) should be
> lowercase "pi".
>>> And finally, a related question: is there a way to get a human-readable
>>> description for a reserved name (e.g. "zoo" -> "complex infinity")? I
>>> didn't find anything in the API for this.
>> Not sure how to go further than
>>>>> type(oo)
>> <class 'sympy.core.numbers.Infinity'>
> Aaaahh, the type! Nice.
> Thanks!
You can also access the attributes of the type, e.g. type(oo).__name__
> The final code that I ended up using (modulo variable naming and
> comments) is:
Using eval() is dangerous, and should be avoided. Here you just need getattr(sy.S, name).
BTW, whenever you want to put a lambda inside a map, you should really use a list comprehension instead, as it's much more readable, and can be combined with a filter, as in:
objs = [getattr(sy.S, name) for name in dis(sy.S) if not name.startswith("__")]
strs = [str(obj) for obj in objs if not obj.is_Number]
(NB: I don't think this is exactly the right code, cf. my first comment)
> descs = {}
> for obj,name in zip(objs,strs):
> descs[name] = str(type(obj))
> # now:
> # - strs[] contains all reserved atoms, except literal numbers
> # - descs{} contains a human-readable description of each item.
> ---8<---8<---8<---
> I would like to suggest adding an API method for this (e.g. with the
> is_Number check made optional), unless my use case is too specific for
> general use :)
> So, dir(S) is the preferred way of getting a complete list :)
The problem with S is that code analysis tools find references to the names in S but don't find them in S because the names are created at runtime.
I think it's desirable to fix this, and while it would be desirable to keep S' API, it might be necessary to change it.
Unless anybody comes up with something better, I guess dict(S) is still your best bet for now.
I'd also like to point out that I'm not aware that "making S more static" is on anybody's agenda. I have been meaning to work on that a bit (by way of discussing the issues), but I never had the time to really commit to such an undertaking. It's possible that it won't ever happen.
>> So, dir(S) is the preferred way of getting a complete list :)
>> It also works in both old and new versions, so that's good. I think the
>> only remaining question is if this is likely to stay working for the
>> foreseeable future?
> Actually, if you use sympify(), almost everything in the sympy
> namespace is handled specially ('C' is probably the only significant
> exception, for obscure reasons), so IIUC the list you want is
> dir(sympy). The way parsing works now is hackish and probably unsafe,
> so don't count on it remaining the same.
Ah, thanks.
dir(sympy) looks applicable at least on first glance.
I think you did understand correctly - the aim is to obtain a list of
reserved strings which sympify() will parse into something else than a
generic Symbol. This list is then used to ensure that any user-defined
quantities (in the FEM solver) will parse as expected, before the
user-given expressions are given to SymPy.
It seems, though, that just the string is not enough information. For
example, in 0.7.x there is the LT() function (to get the leading term),
but e.g. the following is nevertheless valid:
expr = sy.sympify("LT*a") # no error, expr will have Symbols "LT" and "a"
# artificial example: let's replace the symbol "LT" by sin(x)
my_LT = sy.symbols("LT") # refer to the Symbol "LT", not function LT()
expr.subs( {my_LT : sy.sympify("sin(x)")} )
So maybe functions should be ignored? But on the third hand,
expr = sy.sympify("sin*a")
triggers a TypeError; "sin" is recognized as the standard sine function
even if it has no parentheses to denote a function call. (At least in
0.6.7; I have 0.7.1 only at home, so can't check it right now.)
So it seems there is no general rule?
(The way I ran into this was that when developing with 0.6.x, I happened
to decide that I would use the prefix "L" to denote the Laplacian of a
quantity in my equation processor. Then, running the code on 0.7.1
later, with a quantity named "T", produced a surprise...)
>>>> And finally, a related question: is there a way to get a
>>>> human-readable
>>>> description for a reserved name (e.g. "zoo" -> "complex infinity")? I
>>>> didn't find anything in the API for this.
>>> Not sure how to go further than
>>>>>> type(oo)
>>> <class 'sympy.core.numbers.Infinity'>
>> Aaaahh, the type! Nice.
>> Thanks!
> You can also access the attributes of the type, e.g. type(oo).__name__
Ok. Thanks for the tip.
>> The final code that I ended up using (modulo variable naming and
>> comments) is:
>> ---8<---8<---8<---
>> rawnames = filter( lambda name: re.match("__", name) is None, >> dir(sy.S) )
>> objs = map( lambda name: eval("sy.S.%s" % name), rawnames )
>> objs = filter( lambda obj: not obj.is_Number, objs )
>> strs = map( lambda obj: str(obj), objs )
> Using eval() is dangerous, and should be avoided. Here you just need
> getattr(sy.S, name).
Ah, of course. Thanks.
> BTW, whenever you want to put a lambda inside a map, you should really
> use a list comprehension instead, as it's much more readable, and can
> be combined with a filter, as in:
> objs = [getattr(sy.S, name) for name in dis(sy.S) if not
> name.startswith("__")]
> strs = [str(obj) for obj in objs if not obj.is_Number]
> (NB: I don't think this is exactly the right code, cf. my first comment)
Ok. I think both ways are equally readable, but if the list
comprehension is more Pythonic, then why not.
Thanks for this tip, too. I've been programming for a long time, but
only started Python recently :)
>>> So, dir(S) is the preferred way of getting a complete list :)
>>> It also works in both old and new versions, so that's good. I think the
>>> only remaining question is if this is likely to stay working for the
>>> foreseeable future?
>> Actually, if you use sympify(), almost everything in the sympy
>> namespace is handled specially ('C' is probably the only significant
>> exception, for obscure reasons), so IIUC the list you want is
>> dir(sympy). The way parsing works now is hackish and probably unsafe,
>> so don't count on it remaining the same.
> Ah, thanks.
> dir(sympy) looks applicable at least on first glance.
> I think you did understand correctly - the aim is to obtain a list of
> reserved strings which sympify() will parse into something else than a
> generic Symbol. This list is then used to ensure that any user-defined
> quantities (in the FEM solver) will parse as expected, before the
> user-given expressions are given to SymPy.
> It seems, though, that just the string is not enough information. For
> example, in 0.7.x there is the LT() function (to get the leading term),
> but e.g. the following is nevertheless valid:
> expr = sy.sympify("LT*a") # no error, expr will have Symbols "LT" and "a"
> # artificial example: let's replace the symbol "LT" by sin(x)
> my_LT = sy.symbols("LT") # refer to the Symbol "LT", not function LT()
> expr.subs( {my_LT : sy.sympify("sin(x)")} )
This works in the latest version. The reason is that LT was not added
until 0.7.0.
> So maybe functions should be ignored? But on the third hand,
> expr = sy.sympify("sin*a")
> triggers a TypeError; "sin" is recognized as the standard sine function
> even if it has no parentheses to denote a function call. (At least in
> 0.6.7; I have 0.7.1 only at home, so can't check it right now.)
> So it seems there is no general rule?
> (The way I ran into this was that when developing with 0.6.x, I happened
> to decide that I would use the prefix "L" to denote the Laplacian of a
> quantity in my equation processor. Then, running the code on 0.7.1
> later, with a quantity named "T", produced a surprise...)
>>>>> And finally, a related question: is there a way to get a
>>>>> human-readable
>>>>> description for a reserved name (e.g. "zoo" -> "complex infinity")? I
>>>>> didn't find anything in the API for this.
>>>> Not sure how to go further than
>>>>>>> type(oo)
>>>> <class 'sympy.core.numbers.Infinity'>
>>> Aaaahh, the type! Nice.
>>> Thanks!
>> You can also access the attributes of the type, e.g. type(oo).__name__
> Ok. Thanks for the tip.
>>> The final code that I ended up using (modulo variable naming and
>>> comments) is:
>>> ---8<---8<---8<---
>>> rawnames = filter( lambda name: re.match("__", name) is None,
>>> dir(sy.S) )
>>> objs = map( lambda name: eval("sy.S.%s" % name), rawnames )
>>> objs = filter( lambda obj: not obj.is_Number, objs )
>>> strs = map( lambda obj: str(obj), objs )
>> Using eval() is dangerous, and should be avoided. Here you just need
>> getattr(sy.S, name).
> Ah, of course. Thanks.
>> BTW, whenever you want to put a lambda inside a map, you should really
>> use a list comprehension instead, as it's much more readable, and can
>> be combined with a filter, as in:
>> objs = [getattr(sy.S, name) for name in dis(sy.S) if not
>> name.startswith("__")]
>> strs = [str(obj) for obj in objs if not obj.is_Number]
>> (NB: I don't think this is exactly the right code, cf. my first comment)
> Ok. I think both ways are equally readable, but if the list
> comprehension is more Pythonic, then why not.
> Thanks for this tip, too. I've been programming for a long time, but
> only started Python recently :)
> -J
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sympy@googlegroups.com.
> To unsubscribe from this group, send email to sympy+unsubscribe@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
> Am 29.08.2012 13:56, schrieb Juha Jeronen:
>> So, dir(S) is the preferred way of getting a complete list :)
> The problem with S is that code analysis tools find references to the
> names in S but don't find them in S because the names are created at
> runtime.
> I think it's desirable to fix this, and while it would be desirable to
> keep S' API, it might be necessary to change it.
> Unless anybody comes up with something better, I guess dict(S) is
> still your best bet for now.
> I'd also like to point out that I'm not aware that "making S more
> static" is on anybody's agenda. I have been meaning to work on that a
> bit (by way of discussing the issues), but I never had the time to
> really commit to such an undertaking. It's possible that it won't ever
> happen.
> On Thu, Aug 30, 2012 at 12:41 AM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>> It seems, though, that just the string is not enough information. For
>> example, in 0.7.x there is the LT() function (to get the leading term),
>> but e.g. the following is nevertheless valid:
>> expr = sy.sympify("LT*a") # no error, expr will have Symbols "LT" and "a"
>> # artificial example: let's replace the symbol "LT" by sin(x)
>> my_LT = sy.symbols("LT") # refer to the Symbol "LT", not function LT()
>> expr.subs( {my_LT : sy.sympify("sin(x)")} )
> This works in the latest version. The reason is that LT was not added
> until 0.7.0.
Ah, I was originally running on 0.7.1, which does have LT(), but it
turns out there was a mistake in the above artificial example.
What my original code actually did in the case I tested, was only the
last two lines. The my_LT = ... line does work on 0.7.1, but as you say,
it's not possible to sympify() strings containing "LT" as a custom
symbol; instead, the string "LT" will always be interpreted as referring
to the function LT().
The catch is that while I did use sympify() in the original code, the
expression did not contain any "LT". If it did, the TypeError would have
been triggered... so there is a bug in my code. Thanks for discovering it ;)
(The using code has later been optimized so that it won't do unnecessary
subs(); the artificial example was based on how I recalled to have
discovered this.)
So, please disregard my previous comment - correcting, it seems the
string alone *is* enough information to decide whether the name is
reserved or not.
Thus, dir(sympy) and some processing should give a proper list...
This seems to work, but probably it's still not completely robust.
---8<---8<---8<---
def method2(ignore_numbers=True):
"""Get reserved names in SymPy.
Method 2: dir(sympy), and add "Q" and "O".
Return value: (strs, descs), where
- strs[] contains names of all reserved symbols
(except literal numbers if ignore_numbers is True)
- descs{} contains a human-readable description of each item,
keyed by the items in strs[].
Parameters:
ignore_numbers = bool. If True, ignore literal numbers such as -1, 0, 1/2, 1.
Default True.
"""
objs = [getattr(sy, name) for name in dir(sy) if not name.startswith("__")]
if ignore_numbers:
objs = [obj for obj in objs if (not hasattr(obj, "is_Number")
or not obj.is_Number) ]
# For most objects, we can extract the string recognized by sympify() with str(obj).
#
# However, for functions and classes we must do something else.
#
def get_name(obj):
s = str(obj)
if not s.startswith('<'):
return s
else:
if hasattr(obj, "__name__"):
return obj.__name__
elif hasattr(obj, "__class__"):
return str(obj.__class__) # e.g. sympy.C
else:
raise NotImplementedError(s) # TODO: reasonable fallback
strs = map( get_name, objs )
# Handle the set "Q-COSINE".
#
# C parses to Symbol, so strictly speaking it is not reserved
# in the sense meant here. Hence, we leave it out.
#
# dir(sy) misses "Q" and "O", so we need to handle only those.
#
for s in "QO":
objs.append( sy.sympify(s) )
strs.append( s )
# Generate human-readable descriptions, indicating the module
# where the symbol comes from, and its name.
#
descs = {}
for obj,name in zip(objs,strs):
m = "%s." % obj.__module__ if hasattr(obj, "__module__") else ""
n = obj.__name__ if hasattr(obj, "__name__") else str(obj)
descs[name] = "%s%s" % (m,n)
> On 30/08/12 20:16, Aaron Meurer wrote:
>> On Thu, Aug 30, 2012 at 12:41 AM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>>> It seems, though, that just the string is not enough information. For
>>> example, in 0.7.x there is the LT() function (to get the leading term),
>>> but e.g. the following is nevertheless valid:
>>> expr = sy.sympify("LT*a") # no error, expr will have Symbols "LT" and "a"
>>> # artificial example: let's replace the symbol "LT" by sin(x)
>>> my_LT = sy.symbols("LT") # refer to the Symbol "LT", not function LT()
>>> expr.subs( {my_LT : sy.sympify("sin(x)")} )
>> This works in the latest version. The reason is that LT was not added
>> until 0.7.0.
> Ah, I was originally running on 0.7.1, which does have LT(), but it
> turns out there was a mistake in the above artificial example.
> What my original code actually did in the case I tested, was only the
> last two lines. The my_LT = ... line does work on 0.7.1, but as you say,
> it's not possible to sympify() strings containing "LT" as a custom
> symbol; instead, the string "LT" will always be interpreted as referring
> to the function LT().
> The catch is that while I did use sympify() in the original code, the
> expression did not contain any "LT". If it did, the TypeError would have
> been triggered... so there is a bug in my code. Thanks for discovering it ;)
> (The using code has later been optimized so that it won't do unnecessary
> subs(); the artificial example was based on how I recalled to have
> discovered this.)
> So, please disregard my previous comment - correcting, it seems the
> string alone *is* enough information to decide whether the name is
> reserved or not.
> Thus, dir(sympy) and some processing should give a proper list...
And here's a fixed version. The code I just posted returned some nonsensical results, because it didn't filter out modules and classes.
Apart from that, it seems that sympy.core.core.ClassRegistry must be filtered out separately, and sympy.ntheory.generate.Sieve (a classobj with no __name__ or __class__, neither str() nor repr() matching the sympifiable string) must be handled as a special case.
I updated the version in my repo. This has been tested on SymPy 0.7.1.
For convenience, the relevant part is included below (in case someone else needs this). An API function for retrieving the reserved strings would be nice, but I guess this hack will do for now :)
Thanks for the help on this, everyone!
---8<--8<---8<---
import sympy as sy
def method2(ignore_numbers=True):
"""Get reserved names in SymPy.
Method 2: dir(sympy), and add "Q" and "O".
Return value: (strs, descs), where
- strs[] contains names of all reserved symbols
(except literal numbers if ignore_numbers is True)
- descs{} contains a human-readable description of each item,
keyed by the items in strs[].
Parameters:
ignore_numbers = bool. If True, ignore literal numbers such as -1, 0, 1/2, 1.
Default True.
"""
objs = [getattr(sy, name) for name in dir(sy) if not name.startswith("__")]
if ignore_numbers:
objs = [obj for obj in objs if (not hasattr(obj, "is_Number")
or not obj.is_Number) ]
# ignore module and class objects
from types import ModuleType
from types import ClassType
objs = [obj for obj in objs if not isinstance(obj, ModuleType) and not isinstance(obj, ClassType)]
# ignore sympy.C
from sympy.core.core import ClassRegistry
objs = [obj for obj in objs if not isinstance(obj, ClassRegistry) ]
# For most objects, we can extract the string recognized by sympify() with str(obj).
#
# However, for functions and classobjs we must do something else.
#
def get_name(obj):
s = str(obj)
if not s.startswith('<'):
return s
else:
if hasattr(obj, "__name__"):
return obj.__name__
else:
# XXX HACK: sympy.ntheory.generate.Sieve is of type classobj
# and has no __name__ or __class_.
# Its repr() is "<Sieve with X primes sieved: ...>";
# we trigger on that.
#
if repr(obj).find("Sieve"):
return "Sieve"
# Handle the set "Q-COSINE".
#
# C parses to Symbol, so strictly speaking it is not reserved
# in the sense meant here. Hence, we leave it out.
#
# dir(sy) misses "Q" and "O", so we need to handle only those.
#
for s in "QO":
objs.append( sy.sympify(s) )
strs.append( s )
# Generate human-readable descriptions, indicating the module
# where the symbol comes from, and its name.
#
descs = {}
for obj,name in zip(objs,strs):
m = "%s." % obj.__module__ if hasattr(obj, "__module__") else ""
n = obj.__name__ if hasattr(obj, "__name__") else str(obj)
descs[name] = "%s%s" % (m,n)
On Fri, Aug 31, 2012 at 3:30 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
> Hi all (again),
> And here's a fixed version. The code I just posted returned some nonsensical
> results, because it didn't filter out modules and classes.
Another way to maybe approach this problem (getting a non-clashing
form of an expression sympified) is this:
* the user must know what functions they are using: they/you give a
list of these and this list must agree with the representation of that
function's name in sympy
* they give an expressions
* let python parse this (I don't recall which does that now -- there's
some module for parsing python code character by character) and when
it identifies a variable either 1) it is a function that has already
been identified or it is intended as a symbol and 2a) sympy agrees or
2b) sympy disagrees and wants to make it a class or anything other
than a symbol.
* in case of 2b the user's symbol must be de-clashed, by appending
underscores until the clash goes away; this symbol replaces what the
user chose
* the modified input function is returned.
> Apart from that, it seems that sympy.core.core.ClassRegistry must be
> filtered out separately, and sympy.ntheory.generate.Sieve (a classobj
> with no __name__ or __class__, neither str() nor repr() matching the
> sympifiable string) must be handled as a special case.
That one is supposed to be an implementation detail. It's providing the implementation for a singleton, namely "the" Sieve of Erasthotenes.
That's the view for SymPy-using code anyway; some tests that check caching internals of Sieve do indeed create multiple instances.
I suspect you don't want such classes in your results.
However, I have not idea how to detect that category. Checking for a __name__ or __class__ might fit or not; maybe some better convention exists, or should be created. Aaron or Ondrej should be able to answer that.
> On Fri, Aug 31, 2012 at 3:30 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>> Hi all (again),
>> And here's a fixed version. The code I just posted returned some nonsensical
>> results, because it didn't filter out modules and classes.
> Another way to maybe approach this problem (getting a non-clashing
> form of an expression sympified) is this:
> * the user must know what functions they are using: they/you give a
> list of these and this list must agree with the representation of that
> function's name in sympy
> * they give an expressions
> * let python parse this (I don't recall which does that now -- there's
> some module for parsing python code character by character) and when
> it identifies a variable either 1) it is a function that has already
> been identified or it is intended as a symbol and 2a) sympy agrees or
> 2b) sympy disagrees and wants to make it a class or anything other
> than a symbol.
> * in case of 2b the user's symbol must be de-clashed, by appending
> underscores until the clash goes away; this symbol replaces what the
> user chose
> * the modified input function is returned.
Thanks for the idea! I hadn't considered this approach.
This would be pretty easy to do the other way around: undefined symbols are not allowed in my particular use case, and the code already builds a list of user-defined symbols. Anything not explicitly defined is intended to be taken from the library...
Related to which, I have one more question. In 0.6.x sympify() used to add an undefined_Function attribute to unknown functions in the expression:
import sympy as sy
s = sy.sympify("a*f(x) + b + c*x")
L = list(s.atoms(sy.Function))
f = L[0]
hasattr(f, "undefined_Function")
=> True
but 0.7.x no longer does this.
Is it possible to dynamically detect in the new version whether a function object is evaluatable or not, and user-defined or not? At least I couldn't find anything obvious in the attributes...
What I'd like to do with this is to check that all function references in a user-given expression refer to existing library functions only. I could utilize the get-reserved-symbols-by-dir(sympy) hack and check the object type, but that's hardly an elegant solution.
> On 31.08.2012 13:03, Chris Smith wrote:
>> On Fri, Aug 31, 2012 at 3:30 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>>> Hi all (again),
>>> And here's a fixed version. The code I just posted returned some nonsensical
>>> results, because it didn't filter out modules and classes.
>> Another way to maybe approach this problem (getting a non-clashing
>> form of an expression sympified) is this:
>> * the user must know what functions they are using: they/you give a
>> list of these and this list must agree with the representation of that
>> function's name in sympy
>> * they give an expressions
>> * let python parse this (I don't recall which does that now -- there's
>> some module for parsing python code character by character) and when
>> it identifies a variable either 1) it is a function that has already
>> been identified or it is intended as a symbol and 2a) sympy agrees or
>> 2b) sympy disagrees and wants to make it a class or anything other
>> than a symbol.
The tokenize module will split a string of valid python code into its
tokens, which you can then search for names. Or if you don't need 2.5
support, you can use the ast module.
If you want, you could then sympify the name and see if you get a
Symbol or not (caching the results).
>> * in case of 2b the user's symbol must be de-clashed, by appending
>> underscores until the clash goes away; this symbol replaces what the
>> user chose
>> * the modified input function is returned.
> Thanks for the idea! I hadn't considered this approach.
> This would be pretty easy to do the other way around: undefined symbols are not allowed in my particular use case, and the code already builds a list of user-defined symbols. Anything not explicitly defined is intended to be taken from the library...
> Related to which, I have one more question. In 0.6.x sympify() used to add an undefined_Function attribute to unknown functions in the expression:
> import sympy as sy
> s = sy.sympify("a*f(x) + b + c*x")
> L = list(s.atoms(sy.Function))
> f = L[0]
> hasattr(f, "undefined_Function")
> => True
> but 0.7.x no longer does this.
> Is it possible to dynamically detect in the new version whether a function object is evaluatable or not, and user-defined or not? At least I couldn't find anything obvious in the attributes...
In 0.7.2 you will be able to check if it is a subclass of
UndefinedFunction. I don't know if that works in 0.7.1 or what would
be a work around if not.
> What I'd like to do with this is to check that all function references in a user-given expression refer to existing library functions only. I could utilize the get-reserved-symbols-by-dir(sympy) hack and check the object type, but that's hardly an elegant solution.
> -J
> --
> You received this message because you are subscribed to the Google Groups "sympy" group.
> To post to this group, send email to sympy@googlegroups.com.
> To unsubscribe from this group, send email to sympy+unsubscribe@googlegroups.com.
> For more options, visit this group at http://groups.google.com/group/sympy?hl=en.
> On Aug 31, 2012, at 5:12 AM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>> On 31.08.2012 13:03, Chris Smith wrote:
>>> On Fri, Aug 31, 2012 at 3:30 PM, Juha Jeronen <juha.jero...@jyu.fi> wrote:
>>>> Hi all (again),
>>>> And here's a fixed version. The code I just posted returned some nonsensical
>>>> results, because it didn't filter out modules and classes.
>>> Another way to maybe approach this problem (getting a non-clashing
>>> form of an expression sympified) is this:
>>> * the user must know what functions they are using: they/you give a
>>> list of these and this list must agree with the representation of that
>>> function's name in sympy
>>> * they give an expressions
>>> * let python parse this (I don't recall which does that now -- there's
>>> some module for parsing python code character by character) and when
>>> it identifies a variable either 1) it is a function that has already
>>> been identified or it is intended as a symbol and 2a) sympy agrees or
>>> 2b) sympy disagrees and wants to make it a class or anything other
>>> than a symbol.
> The tokenize module will split a string of valid python code into its
> tokens, which you can then search for names. Or if you don't need 2.5
> support, you can use the ast module.
> If you want, you could then sympify the name and see if you get a
> Symbol or not (caching the results).
Ok. Thanks for the tip.
The ast module should be fine.
>> Is it possible to dynamically detect in the new version whether a function object is evaluatable or not, and user-defined or not? At least I couldn't find anything obvious in the attributes...
> In 0.7.2 you will be able to check if it is a subclass of
> UndefinedFunction. I don't know if that works in 0.7.1 or what would
> be a work around if not.
Ok. Thanks.
It seems 0.7.1 doesn't have an UndefinedFunction. But I suppose I can
wait for 0.7.2 for this. (It's not absolutely critical, though it
improves user-friendliness by catching errors early.)