Simplify in sympy with custom symbols - python

Suppose that I have sympy variables x, y and z
I have the expression (x + y) (x + y) + x
Is it possible that I am able to define z as x + y so that I can "simplify" the expression above to z^2 + x?
That is, I want to use z wherever I can in the simplified expression.

Yes, use subs as in
>>> var('x:z')
(x, y, z)
>>> ((x + y)*(x + y) + x).subs(x+y,z)
x + z**2
The subs routine will sometimes try to be smart about substitutions, but works best when the substitution target is a symbol (and hence unambiguous). So if you want to try the replacement x+y->z in a more ambiguous expression, try replacing x->z-y, simplify, and then resubstitute z-y->x to restore anything that didn't change:
>>> ((x*x + 2 * x * y + y * y) + x).subs(x,z-y).expand().subs(z-y,x)
x + z**2

Related

How to substitute variable in multiple equations in Sympy

for example, if I want to use solve a set of linear equations
eq1: x + y + 8z = 2
eq2: 2x + 6y + z = 5
suppose I already know the value of z, is there any way I can subsitute the z in eq1 and eq2 in one operation such as
linear_equations([eq1, eq2]).subs({z: 100})
You can use the Sympy library to achieve what exactly you are looking to solve.
Below is the code which will perform the substitution of the Z value and then solve the linear equation to find the values of x & Y values
from sympy import symbols
from sympy.solvers import solve
x,y,z = symbols('x y z')
expression1 = x + y + 8*z - 2
expression2 = 2*x + 6*y + z - 5
expression1 = expression1.subs(z,100)
expression2 = expression2.subs(z,100)
solution = solve([expression1, expression2], [x, y])
print(solution)

I got an empty list when solving two equation

I tried to solve two simple equations but I got nothing.
from sympy import *
x, y = symbols('x y')
eq1=Function('eq1')
eq2=Function('eq2')
eq1 = Eq(x + y , 1) # x + y = 1
eq2 = Eq(x + y ,3) # x + y = 3
ans = solve([eq1, eq2] , [x, y])
print(ans)
I got
[]
You set everything up ok. The empty list is the way that solve tells you that it could not find a solution for x and y that satisfied the equations. And, indeed, there are no values which, when added, will give two different results (as others have noted).

In Sympy, how do I define a generic function like f(x), such that sympy.diff(f(x), x) returns f' rather than 0.

I am trying to take derivatives of this function
x, y, z, P, k, q = sp.symbols('x y z P k q')
expr = sp.exp(-sp.I*(P+k/(2*q)*(x**2 + y**2)))
where P and q are functions of z. How can I define P and q such that sp.diff(P, z) returns P' rather than 0?
From what you wrote, sympy can't know P and q are functions of z, can it? So it's treating them as constants - just like all the other variables except z. Your expression does not mention z at all, so it is all a constant expression - and the derivation of a constant is 0, no exceptions.
Make sure sympy knows P and q are functions of z. And obviously, it matters what those functions are - you can't just leave them blank. A square differentiates differently than a square root. If you don't know, sympy will do the best it can:
x, y, z, k = sp.symbols('x y z k')
P = sp.Function('P')
q = sp.Function('q')
expr = sp.exp(-sp.I*(P(z)+k/(2*q(z))*(x**2 + y**2)))
sp.diff(expr, z)
# => -I*(-k*(x**2 + y**2)*Derivative(q(z), z)/(2*q(z)**2) + Derivative(P(z), z))*
# exp(-I*(k*(x**2 + y**2)/(2*q(z)) + P(z)))
but if you do know, it can calculate it exactly:
x, y, z, k = sp.symbols('x y z k')
P = sp.Lambda(z, z * z)
q = sp.Lambda(z, sp.sqrt(z))
expr = sp.exp(-sp.I*(P(z)+k/(2*q(z))*(x**2 + y**2)))
sp.diff(expr, z)
# => -I*(-k*(x**2 + y**2)/(4*z**(3/2)) + 2*z)*
# exp(-I*(k*(x**2 + y**2)/(2*sqrt(z)) + z**2))
Similarly, I don't think you can differentiate P, but this works:
sp.diff(P(z), z)
# => 2*z
You can use idiff to get a fragile result with unevaluated derivatives: dPdz = idiff(expr, (P, q), z). It is fragile in the sense that dPdz.doit() will give 0 because there is no explicit dependence on z for q.
>>> idiff(expr,(P,q),z)
k*(x**2 + y**2)*Derivative(q, z)/(2*q**2)
>>> _.doit()
0

how do I make a numpy.piecewise function of arbitrary length? (having lambda issues)

I'm trying to plot a piecewise fit to my data, but I need to do it with an arbitrary number of line segments. Sometimes there are three segments; sometimes there are two. I'm storing the coefficients of the fit in actable and the bounds on the segments in btable.
Here are example values of my bounds:
btable = [[0.00499999989, 0.0244274978], [0.0244275965, 0.0599999987]]
Here are example values of my coefficients:
actable = [[0.0108687987, -0.673182865, 14.6420775], [0.00410866373, -0.0588355861, 1.07750032]]
Here's what my code looks like:
rfig = plt.figure()
<>various other plot specifications<>
x = np.arange(0.005, 0.06, 0.0001)
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x=x: np.log10(actable[j][0] + actable[j][2] * x + actable[j][2] * x**2) for j in list(range(len(actable)))])
plt.plot(x, y)
The problem is that lambda sets itself to the last instance of the list, so it uses the coefficients for the last segment for all the segments. I don't know how to do a piecewise function without using lambda.
Currently, I'm cheating by doing this:
if len(btable) == 2:
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x: np.log10(actable[0][0] + actable[0][1] * x + actable[0][2] * x**2), lambda x: np.log10(actable[1][0] + actable[1][1] * x + actable[1][2] * x**2)])
else if len(btable) == 3:
y = np.piecewise(x, [(x >= btable[i][0]) & (x <= btable[i][1]) for i in range(len(btable))], [lambda x: np.log10(actable[0][0] + actable[0][1] * x + actable[0][2] * x**2), lambda x: np.log10(actable[1][0] + actable[1][1] * x + actable[1][2] * x**2), lambda x: np.log10(actable[2][0] + actable[2][1] * x + actable[2][2] * x**2)])
else
print('Oh no! You have fewer than 2 or more than 3 segments!')
But this makes me feel icky on the inside. I know there must be a better solution. Can someone help?
This issue is common enough that Python's official documentation has an article Why do lambdas defined in a loop with different values all return the same result? with a suggested solution: create a local variable to be initialized by the loop variable, to capture the changing values of the latter within the function.
That is, in the definition of y it suffices to replace
[lambda x=x: np.log10(actable[j][0] + actable[j][1] * x + actable[j][2] * x**2) for j in range(len(actable))]
by
[lambda x=x, k=j: np.log10(actable[k][0] + actable[k][1] * x + actable[k][2] * x**2) for j in range(len(actable))]
By the way, one can use one-sided inequalities to specify ranges for numpy.piecewise: the last of the conditions that evaluate to True will trigger the corresponding function. (This is a somewhat counterintuitive priority; using the first true condition would be more natural, like SymPy does). If the breakpoints are arranged in increasing order, then one should use "x>=" inequalities:
breaks = np.arange(0, 10) # breakpoints
coeff = np.arange(0, 20, 2) # coefficients to use
x = np.arange(0, 10, 0.1)
y = np.piecewise(x, [x >= b for b in breaks], [lambda x=x, a=c: a*x for c in coeff])
Here each coefficient will be used for the interval that begins with the corresponding breakpoint; e.g., coefficient c=0 is used in the range 0<=x<1, coefficient c=2 in the range 1<=x<2, and so on.

What is this piece of Python code doing?

This following is a snippet of Python code I found that solves a mathematical problem. What exactly is it doing? I wasn't too sure what to Google for.
x, y = x + 3 * y, 4 * x + 1 * y
Is this a special Python syntax?
x, y = x + 3 * y, 4 * x + 1 * y
is the equivalent of:
x = x + 3 * y
y = 4 * x + 1 * y
EXCEPT that it uses the original values for x and y in both calculations - because the new values for x and y aren't assigned until both calculations are complete.
The generic form is:
x,y = a,b
where a and b are expressions the values of which get assigned to x and y respectively. You can actually assign any tuple (set of comma-separated values) to any tuple of variables of the same size - for instance,
x,y,z = a,b,c
would also work, but
w,x,y,z = a,b,c
would not because the number of values in the right-hand tuple doesn't match the number of variables in the left-hand tuple.
It's an assignment to a tuple, also called sequence unpacking. Probably it's clearer when you add parenthesis around the tuples:
(x, y) = (x + 3 * y, 4 * x + 1 * y)
The value x + 3 * y is assigned to x and the value 4 * x + 1 * y is assigned to y.
It is equivalent to this:
x_new = x + 3 * y
y_new = 4 * x + 1 * y
x = x_new
y = y_new
I also recently saw this referred to as "simultaneous assignment", which seems to capture the spirit of several of the answers.

Categories