Converting Dummy symbols to Symbols in Sympy - sympy

How can I convert Dummy variables (sympy.core.symbol.Dummy) to regular symbols in Sympy?
For example, say we want to find all vectors (x_1,x_2) in the kernel of a matrix that satisfying some equation f(x_1,x_2) = 0.
I would break it into two steps. First:
from sympy import Matrix
M = Matrix( [[1,0],
[0,0] ])
zeros = Matrix([[0],
[0]])
sol = M.gauss_jordan_solve(zeros)[0]
Second: solve f(sol) = 0.
But I don't know how to tell Sympy to treat the entries of sol as symbols. Any ideas?

sol.free_symbols returns a set of all symbols in sol ("free" is a detail that does not matter on this occasion). If you'd like to replace them with some other symbols of your choice, then create new symbols (as many as needed) and replace using subs, as shown below.
from sympy import symbols
dummies = list(sol.free_symbols)
my_syms = symbols("x0:{}".format(len(dummies))) # Example: symbols("x0:3") creates x0, x1, x2
sol = sol.subs(dict(zip(dummies, my_syms)))

Related

Substitute numerical constants with symbols in sympy

I have a question similar to this one: How to substitute multiple symbols in an expression in sympy? but in reverse.
I have a sympy expression with numerical values and symbols alike. I would like to substitute all numerical values with symbolic constants. I appreciate that such query is uncommon for sympy. What can I try next?
For example, I have:
-0.5967695*sin(0.15280747*x0 + 0.89256966) + 0.5967695*sin(sin(0.004289882*x0 - 1.5390939)) and would like to replace all numbers with a, b, c etc. ideally in a batch type of way.
The goal is to then apply trig identities to simplify the expression.
I'm not sure if there is already such a function. If there is not, it's quite easy to build one. For example:
import string
def num2symbols(expr):
# wild symbol to select all numbers
w = Wild("w", properties=[lambda t: isinstance(t, Number)])
# extract the numbers from the expression
n = expr.find(w)
# get a lowercase alphabet
alphabet = list(string.ascii_lowercase)
# create a symbol for each number
s = symbols(" ".join(alphabet[:len(n)]))
# create a dictionary mapping a number to a symbol
d = {k: v for k, v in zip(n, s)}
return d, expr.subs(d)
x0 = symbols("x0")
expr = -0.5967695*sin(0.15280747*x0 + 0.89256966) + 0.5967695*sin(sin(0.004289882*x0 - 1.5390939))
d, new_expr = num2symbols(expr)
print(new_expr)
# out: b*sin(c + d*x0) - b*sin(sin(a + f*x0))
print(d):
# {-1.53909390000000: a, -0.596769500000000: b, 0.892569660000000: c, 0.152807470000000: d, 0.596769500000000: e, 0.00428988200000000: f}
I feel like dict.setdefault was made for this purpose in Python :-)
>>> c = numbered_symbols('c',cls=Dummy)
>>> d = {}
>>> econ = expr.replace(lambda x:x.is_Float, lambda x: sign(x)*d.setdefault(abs(x),next(c)))
>>> undo = {v:k for k,v in d.items()}
Do what you want with econ and when done (after saving results to econ)
>>> econ.xreplace(undo) == expr
True
(But if you change econ the exact equivalence may no longer hold.) This uses abs to store symbols so if the expression has constants that differ by a sign they will appear in econ with +/-ci instead of ci and cj.

How to sympify initial conditions for ODE in sympy?

I am passing initial conditions as string, to be used to solving an ODE in sympy.
It is a first order ode, so for example, lets take initial conditions as y(0):3 for example. From help
ics is the set of initial/boundary conditions for the differential
equation. It should be given in the form of {f(x0): x1,
f(x).diff(x).subs(x, x2): x3}
I need to pass this to sympy.dsolve. But sympify(ic) gives an error for some reason.
What other tricks to use to make this work? Here is MWE. First one shows it works without initial conditions being string (normal mode of operation)
from sympy import *
x = Symbol('x')
y = Function('y')
ode = Eq(Derivative(y(x),x),1+2*x)
sol = dsolve(ode,y(x),ics={y(0):3})
gives sol Eq(y(x), x**2 + x + 3)
Now the case when ics is string
from sympy import *
ic = "y(0):3"
x = Symbol('x')
y = Function('y')
ode = Eq(Derivative(y(x),x),1+2*x)
sol = dsolve(ode,y(x),ics={ sympify(ic) })
gives
SympifyError: Sympify of expression 'could not parse 'y(0):3'' failed,
because of exception being raised: SyntaxError: invalid syntax
(, line 1)
So looking at sympify webpage
sympify(a, locals=None, convert_xor=True, strict=False, rational=False, evaluate=None)
And tried changing different options as shown above, still the syntax error shows up.
I also tried
sol = dsolve(ode,y(x),ics= { eval(ic) } )
But this gives syntax error as well
Is there a trick to use to convert this initial conditions string to something sympy is happy with?
Python 4.7 with sympy 1.5
As temporary work around, currently I do this
from sympy import *
ic = "y(0):3"
ic = ic.split(":")
x = Symbol('x')
y = Function('y')
ode = Eq(Derivative(y(x),x),1+2*x)
sol = dsolve(ode,y(x),ics= {S(ic[0]):S(ic[1])} )
Which works. So the problem is with : initially, sympify (or S) do not handle : it seems.
You can use sympify('{y(0):3}').
I don't know what your actual goal is but I don't recommend parsing strings like this in general. The format for ICs is actually slightly awkward so that for a second order ODE it looks like:
ics = '{y(0):3, y(x).diff(x).subs(x, 0):1}'
If you're parsing a string then you can come up with a better syntax than that like
ics = "y(0)=3, y'(0)=1"
Also you should use parse_expr rather than converting strings with sympify or S:
https://docs.sympy.org/latest/modules/parsing.html#sympy.parsing.sympy_parser.parse_expr

Initialize a variable number of sympy symbols

Whenever you want to work with the python package sympy, a package for symbolic calculation, you need to initialize the variables as
x, y, z = symbols('x y z')
For my application, the number of symbols, that I need, is not fixed. I only have the information, that I have to calculate with e.g. 4 variables.
Is there a smart way to write to e.g. initialize
a,b,c = symbols('a b c')
when I need three variables and
a,b,c,d,e = symbols('a b c d e')
when I need five variables?
In case, that I need more variables than letters in the alphabet,
the function should start to initialize
aa, ab, ac,... .
You can use slice notation in symbols to create numbered symbols like
In [16]: symbols('a1:100')
Out[16]:
(a₁, a₂, a₃, a₄, a₅, a₆, a₇, a₈, a₉, a₁₀, a₁₁, a₁₂, a₁₃, a₁₄, a₁₅, a₁₆, a₁₇, a₁₈, a₁₉, a₂₀, a₂₁, a₂₂, a₂₃, a₂₄, a₂₅, a₂₆, a₂₇, a
₂₈, a₂₉, a₃₀, a₃₁, a₃₂, a₃₃, a₃₄, a₃₅, a₃₆, a₃₇, a₃₈, a₃₉, a₄₀, a₄₁, a₄₂, a₄₃, a₄₄, a₄₅, a₄₆, a₄₇, a₄₈, a₄₉, a₅₀, a₅₁, a₅₂, a₅₃,
a₅₄, a₅₅, a₅₆, a₅₇, a₅₈, a₅₉, a₆₀, a₆₁, a₆₂, a₆₃, a₆₄, a₆₅, a₆₆, a₆₇, a₆₈, a₆₉, a₇₀, a₇₁, a₇₂, a₇₃, a₇₄, a₇₅, a₇₆, a₇₇, a₇₈, a₇
₉, a₈₀, a₈₁, a₈₂, a₈₃, a₈₄, a₈₅, a₈₆, a₈₇, a₈₈, a₈₉, a₉₀, a₉₁, a₉₂, a₉₃, a₉₄, a₉₅, a₉₆, a₉₇, a₉₈, a₉₉)
Then if you want n symbols where n is an int you can do
syms = symbols('a1:%d' % n)

Sympy gives unexpected differentiation result when the input is a string

Why is the result of the differentiation not 2*x0 in the following code:
In [54]: import sympy
In [55]: x = [sympy.Symbol('x%d' % i, real=True) for i in range(3)]
In [56]: x
Out[56]: [x0, x1, x2]
In [57]: sympy.diff('x0*x0 + x1*x1 + x2*x2',x[0])
Out[57]: 0
First, the creation of multiple numbered symbols is simpler with
x = sympy.symbols('x0:3', real=True) # returns (x0, x1, x2)
Second, the SymPy function to turn a string into a SymPy expression is sympify. This function is called automatically when you provide input as a string; however, this gives you no control over the interpretation of the string, and "unexpected results" are likely.
In this case, SymPy is not sure that "x0" appearing in the string is the same as x0 you created earlier. After all, your x0 has the additional property of being real, and the symbol from the string has no such assumptions on it. It's Symbol('x0') vs Symbol('x0', real=True); not a match.
This is one of many reasons why throwing a string in a SymPy function is a bad idea. Use sympify, and read about its parameters which control the parsing of input. Specifically, locals parameter is a dictionary mapping pieces of the string to objects you already have in SymPy, precisely what is needed here.
locals = {'x{}'.format(i): x[i] for i in range(3)} # {'x0': x0, 'x1': x1, 'x2': x2}
expr = sympy.sympify('x0*x0 + x1*x1 + x2*x2', locals=locals)
Now you can differentiate expr with respect to any symbols and get expected results
[expr.diff(sym) for sym in x] # [2*x0, 2*x1, 2*x2]
(Another benefit of having an expression before trying diff is that you can invoke diff as a method of the expression, saving the trouble of typing sympy. prefix.)
In your declarations, you should use sympy.symbols that is the reference method (from the documentation and tutorial) to declare variables.
x = [sympy.symbols('x%d' % i, real=True) for i in range(3)]
On top of this, you must pick (from experimentations that I made) either a string in both arguments, as:
sympy.diff('x0*x0 + x1*x1 + x2*x2',str(x[0]))
or symbolic expressions on both sides:
sympy.diff(x[0]*x[0] + x[1]*x[1] + x[2]*x[2], x[0])

Selecting elements in numpy array using regular expressions

One may select elements in numpy arrays as follows
a = np.random.rand(100)
sel = a > 0.5 #select elements that are greater than 0.5
a[sel] = 0 #do something with the selection
b = np.array(list('abc abc abc'))
b[b==a] = 'A' #convert all the a's to A's
This property is used by the np.where function to retrive indices:
indices = np.where(a>0.9)
What I would like to do is to be able to use regular expressions in such element selection. For example, if I want to select elements from b above that match the [Aab] regexp, I need to write the following code:
regexp = '[Ab]'
selection = np.array([bool(re.search(regexp, element)) for element in b])
This looks too verbouse for me. Is there any shorter and more elegant way to do this?
There's some setup involved here, but unless numpy has some kind of direct support for regular expressions that I don't know about, then this is the most "numpytonic" solution. It tries to make iteration over the array more efficient than standard python iteration.
import numpy as np
import re
r = re.compile('[Ab]')
vmatch = np.vectorize(lambda x:bool(r.match(x)))
A = np.array(list('abc abc abc'))
sel = vmatch(A)