Alan Liddell
Alan Liddell

Reputation: 179

Classifying free symbols from expressions

Suppose I am given two Symbol objects, f and g, which I can reasonably assume represent functions. I don't know a priori what these two functions are, but say that without my knowledge, g was defined like so:

g = symbols('1')

where the user has neglected to specify cls=Number so that g.free_symbols gives me set([1]) (maybe the user isn't intimately familiar with SymPy). If I collect all the free symbols of f and g in a set, and then attempt to compute their Jacobian, I end up with an extra column in the matrix representing the derivatives of f and g with respect to 1, which is nonsense.

How can I detect numbers as Symbol objects which are not explicitly declared to be numbers so as to avoid such a scenario?

Upvotes: 1

Views: 365

Answers (3)

smichr
smichr

Reputation: 19093

A more targeted approach would use replace:

Target only Numbers

>>> eq.replace(
... lambda x: x.is_Symbol and S(x.name).is_Number,
... lambda x: S(x.name))
x + 1
>>> _.atoms(Number)
set([1])

Target any number expression

>>> Symbol('1+2').replace(
... lambda x: x.is_Symbol and S(x.name).is_Number,
... lambda x: S(x.name))
3

Target any expression

>>> Symbol('1+x').replace(
... lambda x: S(x.name).is_Symbol is False,
... lambda x: S(x.name))
x + 1

Upvotes: 1

smichr
smichr

Reputation: 19093

You could pass the str of the expression through sympify:

>>> eq = Symbol('x') + Symbol('1')
>>> eq.atoms(Number)
set([])
>>> sympify(str(eq))
x + 1
>>> _.atoms(Number)
set([1])

Upvotes: 1

ErikR
ErikR

Reputation: 52049

You could inspect the name of each symbol and see if it looks like a number:

import sympy as sp
import re

x = sp.Symbol('x')
g = sp.Symbol('2')
f = x + 1
h = f + g

for s in h.free_symbols:
  print "s = ", s, bool(re.match('^[0-9]+$', str(s)))

Output:

s =  x False
s =  2 True       -- looks like a number

Perhaps, then, you want to substitute numeric values back for those symbols to eliminate them.

def eliminate_numbers(f):
  for s in f.free_symbols:
    assigns = {}
    if re.match('^-?[0-9]+$', str(s)):
      assigns[s] = int( str(s) )
  return f.subs(assigns)

print eliminate_numbers(h)

Output:

x + 3

Upvotes: 0

Related Questions