How to define a lot of symbols in SymPy - sympy

I am trying to define a lot of variables in "sympy" for symbolic processing.
import sympy as sp
b_0 = sp.symbols('b_0')
b_1 = sp.symbols('b_1')
...
b_X = sp.symbols('b_X')
and so on with the X going from 1 to 1000.
Is there an easy way to do it?

There are a few options:
>>> sp.symbols('b_0:10')
(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)
or, using a formatted string,
>>> n = 10
>>> sp.symbols('b_0:{}'.format(n))
(b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9)
These return a tuple of symbols. There are more formatting options: see symbols docs.
There is also a function to generate a NumPy array of symbols:
>>> sp.symarray('b', 10)
array([b_0, b_1, b_2, b_3, b_4, b_5, b_6, b_7, b_8, b_9], dtype=object)
All of these examples are meant to be assigned to something. For example, b = sp.symbols('b_0:10') assigns the tuple of symbols to b, so they can be accessed as b[0], b[1], etc. SymPy symbols are not accessed by the string representing them, such as "b_0" or "b_1".
Finally, there are Indexed objects in case you need an array of symbols of undetermined size: Indexed objects are created on the fly as you use A[n] notation with A being an IndexedBase.

If you want to still be able to call individuals symbols, like b_0:
Since:
from sympy import symbols
# Number of symbols you need
X = 5
b = symbols(f"b_0:{X}")
>>> b
(b_0, b_1, b_2, b_3, b_4)
>>> b_0
NameError: name 'b_0' is not defined
You could add them to the local variables through a dictionary:
from sympy import symbols
# Number of symbols you need
X = 5
# To still have b[i]
b = symbols(f"b_0:{X}")
b_dict = {f"b_{i}": b[i] for i in range(X)}
locals().update(b_dict)

Related

sympy:Please tell me about the line.equation()

sympy.line.equation()
Same value and type, but what's the difference?"
How can I fix it?
What is the difference between z1 and z2?
from sympy import *
var('x y z1 z2')
z1=8*x+6*y+48
print("#z1",z1,type(z1))
z2=Line(Point(-6,0),Point(0,-8)).equation()
print("#z2",z2,type(z2))
if type(z1)==type(z2):
print("#","type==")
else:
print("#","type<>")
if z1==z2:
print("#","==")
else:
print("#","<>")
#z1 8*x + 6*y + 48 <class 'sympy.core.add.Add'>
#z2 8*x + 6*y + 48 <class 'sympy.core.add.Add'>
# type==
# <>
I try add .expand().simplify() 30 mins ago
from sympy import *
var('x y')
print("#z1#",solve(8*x+6*y+ 48 ,y))
print("# ", Line(Point(-6,0),Point(0,-8)).equation().expand().simplify() )
print("#z2#",solve(Line(Point(-6,0),Point(0,-8)).equation().expand().simplify(),y))
#z1# [-4*x/3 - 8]
# 8*x + 6*y + 48
#z2# []
Thank you.
from sympy import *
var('x y')
print("#z1#",solve(8*x+6*y+ 48 ,y))
print("# ", Line(Point(-6,0),Point(0,-8)).equation() )
print("#z2#",solve(sympify(str(Line(Point(-6,0),Point(0,-8)).equation())),y))
#z1# [-4*x/3 - 8]
# 8*x + 6*y + 48
#z2# [-4*x/3 - 8]
The equality operator (==) in SymPy tests whether expressions have identical form, not whether they are mathematically equivalent.
If you want to determine the mathematical equivalence of nontrivial expressions, you should apply a more advanced simplification routine to both sides of the equation. In the case of polynomials, expressions can be rewritten in a canonical form by expanding them fully. This is done using the .expand() method in SymPy - in your case:
print(bool(z1.expand()==z2.expand()))
or
print((z1-z2).expand())
In first case True will be resulted for equivalent expressions. In second case you will get 0 (zero) if expressions are equivalent. But you will have False and 8x - 8x - 6y + 6y instead.
If you try simplify(), which attempt more advanced transformations, you will get the same result:
print(simplify(z1-z2))
That means that your expressions has same type and 'value', but not mathematically equivalent. See detail here.
When you don't tell equation what symbols to use, it creates its own symbols x and y with assumptions appropriate for a line (i.e. real). When you create a symbol with var the symbols are created with only commutative assumptions. Symbols that have different assumptions do not compare equal.
>>> from sympy import Line, oo
>>> Line((0,0),slope=oo).equation()
x
>>> _._assumptions
{'real': True, 'commutative': True, 'infinite': False, 'extended_real': True,
'hermitian': True, 'complex': True, 'imaginary': False, 'finite': True}
>>> var('x')
x
>>> _._assumptions
{'commutative': True, 'zero': None}
>>> Line((0,0),slope=oo).equation(x) # supply value of x to use
x
>>> _ == x
True
>>> Line((0,0),slope=oo).equation() == x # no default given
False
So...if you want to compare expression built by Line and yourself, use the same symbols (i.e. provide the defaults to Line.equation. Otherwise the hidden assumptions will cause issues.
>>> from sympy.abc import y
>>> Line(Point(-6,0),Point(0,-8)).equation(x,y)
8*x + 6*y + 48
>>> _==8*x + 6*y + 48
True

Create indexed functions in sympy

Is it possible to create indexed functions in sympy like fi(t) which might be used in a product or sum, eg Σfi(t)?
import sympy as sp
f = sp.Function('f')
i = sp.symbols('i', integer=True)
t = sp.symbols('t', real=True)
sp.Indexed(f, i)(t)
The above code produces the following error:
TypeError:
The base can only be replaced with a string, Symbol, IndexedBase or an
object with a method for getting items (i.e. an object with a
`__getitem__` method).
Assuming that you just want graphically pleasing output you can use the following
import sympy as sp
class f(sp.Function):
name='f'
def _latex(self, printer=None):
a = [printer.doprint(i) for i in self.args]
name=self.name
return r'{}_{{{}}}\left('.format(name,a[0])+','.join(a[1:])+r'\right)'
i = sp.symbols('i', integer=True)
t = sp.symbols('t', real=True)
f(i,t)
sp.Sum(f(i,t),(i,0,sp.oo))

how to retrieve an integer index from a sympy symbol

I have a list L of sympy symbols defined using the following function : def x(i): return sy.symbols(f'x({i})')
L=[x(10),x(33),....]. Is there away to get the numbers 10,33,... out of the sympy symbols ?
You can parse the numbers out of the symbol name with any python method that you like, e.g.
>>> x = Symbol('x(10)')
>>> int(x.name.split("(")[1].split(")")[0])
10
Of course, the method of parsing will depend on how you hide the number in the symbol name. It might be easier for you to simply work with indexed symbols:
>>> from sympy import IndexedBase
>>> x = IndexedBase('x')
>>> x[1] + x[1]
2*x[1]
>>> x[1].indices
(1,)

Defining a range for a symbol in Sympy

In Sympy it is possible to define constraints on what values a symbol may take
x = symbols('x', real=True)
Is it possible to say that a symbol should take values only in a certain range, say -1 < x < 1? The reason why I am interested in this is because I am trying to get sympy to automatically simplify expressions like the one below
expr = sqrt(1+x) * sqrt((1-x)*(1+x)) / sqrt(1-x)
Running simplify(expr) yields no simplification, whereas when -1<x<1 the simplified result should be 1+x. How do I get sympy to simplify expressions like the one above?
Although a single symbol can't hold that assumption, an expression can. Let's define an expression that has the desired range:
>>> p = Symbol('p', positive=True)
>>> neg1to1 = (p - 1)/(p + 1)
Now replace x with that value and simplify
>>> asp = expr.subs(x, neg1to1).simplify(); asp
2*p/(p + 1)
Now restore x from the relationship between it and neg1to1:
>>> p_as_x = solve(neg1to1 - x, p)[0]
>>> asp.subs(p, p_as_x).simplify()
x + 1
You could turn this into a function to allow for any range for x:
>>> def simplify_assuming_range(expr, x, lo, hi):
... from sympy import Dummy, solve
... p = Dummy(positive=True)
... xr = (p - 1)/(p + 1)*(hi - lo) + lo
... rx = solve(xr - x, p)[0]
... return expr.subs(x, xr).simplify().subs(p, rx).simplify()
...
>>> simplify_assuming_range(expr,x,-1,1)
x + 1
Using targeted expansion with force can help:
>>> expand(expr, power=True, force=True, mul=False)
x + 1
The expand docstring will tell about each of those options.

Iterating operations over unique values of an array

I have a pandas dataframe that resembles one generated as follows.
import numpy as np
import pandas as pd
x0 = pd.DataFrame(np.random.normal(size=(10, 4)))
x1 = pd.DataFrame({'x': [1,1,2,3,2,3,4,1,2,3]})
df = pd.concat((x0, x1), axis=1)
and a function:
def fun(df, n=100):
z = np.random.normal(size=n)
return np.dot(df[[0,1,2,3]], [0.5*z,-1*z,0.3*z,1.2*z])
I would like to:
use identical draws z for each unique value in x,
take the product of the output in the above step over items of unique x
Any suggestion?
Explanation:
Generate n=100 draws to get z such that len(z)=100
For each elem in z, evaluate the function fun,
For i in df.x.unique(), compute the product of the output in step (2) element-wise. I am expecting to get a DataFrame or array of dimension (len(df.x.unique(), n=100)
4.
It sounds like you want to group by 'x', taking one of its instances (let's assume we take the first one observed).
just call your function as follows:
f = fun(df.groupby('x').first())
>>> f.shape
Out[25]: (4, 100)
>>> len(df.x.unique()
Out[26]: 4