I have a sympy expression involving two variables a, b. I would now like to evaluate this expression for specific values of a and b. Using a lambda like
import sympy
def get_expression(a, b):
# Complex function with a simple result. I have no control here.
return a*b + 2
a = sympy.Symbol('a')
b = sympy.Symbol('b')
z = get_expression(a, b)
f = lambda a, b: z
print(f(1, 1))
only gives
a*b + 2
though.
Any hints?
Turns out that lambdify is what I need:
f = sympy.lambdify([a, b], z)
print(f(1, 1))
Related
I'm trying to solve the cosine formula using sympy. (A is angle and a, b, c is sides)
sol = solve(a**2-b**2-c**2+2*b*c*cos(pi/180*A), [A, a, b, c])
print(sol)
And I end up getting results like this:
[(90.0000000000000, 5.00000000000000, 4.00000000000000, 3.00000000000000), (270.000000000000, 5.00000000000000, 4.00000000000000, 3.00000000000000)]
But it's impossible for a triangle to have a 270º interior angle.
I tried to do A, B, C <= 180 and solve(a**2-b**2-c**2+2*b*c*cos(pi/180*A), [A<=180, a, b, c]).I also try to use exclude=[A<=180] in the solve(), but it don't work too.
Let's understand what's happening:
from sympy import *
a, b, c, A = symbols("a, b, c, A")
eq = a**2-b**2-c**2+2*b*c*cos(pi/180*A)
eq is a trigonometric equation involving the cosine function, which is periodic with a period of 2*pi. Suppose you want to solve cos(x) = y where y is contained in (-1, 1]: you will get two solutions! For example, suppose you want to solve cos(x) = 0.5: one solution is x=60deg and the other is x=300deg.
Now let's focus on your equation. You have one equation with 4 symbols. Let's assume that a,b,c are known quantities and asks Sympy to solve for A (the unknown, which I assume it is the angle in degrees).
We can filter out the solutions in two approaches.
First approach
Substitute the known values a, b, c into eq and then solve for A with a specified constraint:
# substitution dictionary
d = {a: 1, b: 1, c: 1}
# solve the equation asking to find the solution for which A <= 180
sol = solve([eq.subs(d), A <= 180], A)
print(sol)
# out: (-oo < A) & (A <= 180) & (Eq(A, 60) | Eq(A, 300))
Here, SymPy produced a boolean solution (meaning it is a combination of one or more solutions with the And and Or objects). Let's try to simplify it a bit:
sol2 = sol.as_set()
print(sol2)
# out: {60}
Now we can extract it with:
A_num = list(sol2)[0]
print(A_num)
# 60
Second approach
Obtain a fully symbolic solution, then substitute the known values and filter out according to your rules:
sol = solve(eq, A)
for i, s in enumerate(sol):
print("sol", i+1, ":", s)
# output:
# sol 1 : 180*(-acos((-a**2 + b**2 + c**2)/(2*b*c)) + 2*pi)/pi
# sol 2 : 180*acos((-a**2 + b**2 + c**2)/(2*b*c))/pi
# substitution dictionary
d = {a: 1, b: 1, c: 1}
sol = [s.subs(d) for s in sol]
# apply custom filter:
sol = [s for s in sol if s <= 180]
sol = sol[0]
print(sol)
# out: 60
When doing (ad hoc example)
from scipy import optimize
def fit_func(x, a, b):
return a*x + b
optimize.curve_fit(fit_func, x_data, y_data)
how can I put bounds like a>b? I know the option bounds, but it appears it does accept only explicit numbers.
Maybe some if in the definition of fit_func?
You can try with a workaround, instead of defining the function as:
def fit_func1(x, a, b):
return a*x + b
with constraint a>b, you can do:
def fit_func2(x, this_much_a_is_bigger_than_b, b):
return (a+this_much_a_is_bigger_than_b)*x + b
with constraint this_much_a_is_bigger_than_b > 0, 0 being an explicit number, and fit_func2 function is equivalent to the fit_func1 from a mathematical perspective.
Then you can have:
a = b + this_much_a_is_bigger_than_b
b = b
Curce_fit only supports box constraints (via least_squares). There are workarounds, but I'd rather change variables to e.g a and b-a
I have the following function:
import sympy as sp
def inverted(q, m, a, nu):
return (-1)**(m+1)*(a/m)**m*sp.exp(m)*q**(-nu)*sp.diff(1/(sp.sqrt(a**2+q**2))*(sp.sqrt(a**2+q**2)-a)**(nu), a, m+1)
I want to define some lambda function such that
f100 = lambda a, q: inverted(q, 100, a, 0)
However, when I try to examine
q = sp.symbols('q')
f100(1000.0, q)
I get the following output:
ValueError:
Can't calculate 101st derivative wrt 10.
Obviously, what is happening is when I call f100(1000.0, q), the function refers back to inverted and the issue arises. I was hoping for a way around this.
Seems like you have to make a a variable first so diff works. It doesn't work if you fix a before (I think because you differentiate with respect to a). You can substitute a with 1000 afterwards.
import sympy as sp
def inverted(q, m, a, nu):
return (-1)**(m+1)*(a/m)**m*sp.exp(m)*q**(-nu)*sp.diff(1/(sp.sqrt(a**2+q**2))*(sp.sqrt(a**2+q**2)-a)**(nu), a, m+1)
f100 = lambda a, q: inverted(q, 100, a, 0)
q, a = sp.symbols('q, a')
print(f100(a, q).subs(a, 1000))
In an expression like
import sympy
a = sympy.Symbol('a')
b = sympy.Symbol('b')
x = a + 2*b
I'd like to swap a and b to retrieve b + 2*a. I tried
y = x.subs([(a, b), (b, a)])
y = x.subs({a: b, b: a})
but neither works; the result is 3*a in both cases as b, for some reason, gets replaced first.
Any hints?
There is a simultaneous argument you can pass to the substitution, which will ensure that all substitutions happen simultaneously and don't interfere with one another as they are doing now.
y = x.subs({a:b, b:a}, simultaneous=True)
Outputs:
2*a + b
From the docs for subs:
If the keyword simultaneous is True, the subexpressions will not be evaluated until all the substitutions have been made.
I have a scrip that automatically generates equations.
The equations are constructed using sympy symbols.
I would like to know whether or not these is a way to check if the equations are linear in terms of certain variables.
eg.
a, b, c, d = sympy.symbols('a, b, c, d')
eq1 = c*b*a + b*a + a + c*d
check for the following: is eq1 linear in terms of a, d?
True
A function is (jointly) linear in a given set of variables if all second-order derivatives are identically zero (including mixed ones). This can be checked as follows:
def is_linear(expr, vars):
for x in vars:
for y in vars:
try:
if not sympy.Eq(sympy.diff(expr, x, y), 0):
return False
except TypeError:
return False
return True
In the loop, every derivative is taken and checked for equality to zero. If sympy cannot decide if it's zero (raising TypeError) then it's not identically zero.
Output:
>>> is_linear(eq1, [a,d])
True
>>> is_linear(eq1, [a,c])
False
To check for separate linearity (e.g., separately in a and separately in b), drop mixed partial derivatives:
def is_separately_linear(expr, vars):
for x in vars:
try:
if not sympy.Eq(sympy.diff(expr, x, x), 0):
return False
except TypeError:
return False
return True
Output:
>>> is_separately_linear(eq1, [a,d])
True
>>> is_separately_linear(eq1, [a,c])
True
A simpler way would be to check the degree of the expression as a polynomial in each variable.
In [17]: eq1 = c*b*a + b*a + a + c*d
In [18]: degree(eq1, a)
Out[18]: 1
In [19]: degree(eq1, d)
Out[19]: 1
and expression is linear if the polynomial degree is <= 1.
If you know the expression is a polynomial in your variables, you can also just check for powers that contain the variable.
In [21]: [i for i in eq1.atoms(Pow) if i.base == a]
Out[21]: []
In [22]: eq2 = b*a**2 + d + c
In [23]: [i for i in eq2.atoms(Pow) if i.base == a]
Out[23]:
⎡ 2⎤
⎣a ⎦
To expand on the answer from 404, if fxy=0, then fyx=0. Thus, the computation time can be cut in half for the mixed derivatives solution.
from itertools import combinations_with_replacement
def is_linear(expr, variables):
combs = combinations_with_replacement(variables, 2)
try:
return all(sympy.Eq(sympy.diff(expr, *t), 0) for t in combs)
except TypeError:
return False