Pythonic Way to Solve this Matrix - python

I've been thinking on this problem, but I can't seem to wrap my head around it.
I want to solve a matrix with three equations with unknowns x, y, z so they all equal the same number.
Lets say my equations are:
x + 3 = A
y(2y - 2) = 2A
z(4z - 1) = A
So I can construct a matrix looking like:
[(X + 3) , 0 , 0] [0] [A]
[ 0 ,(2y - 2), 0] [y] = [2A]
[ 0 , , 0, (4z -1)] [z] [A]
I know numpy has a linear algebra but that is only when the answer (A) is already known.
My question is, would I have to construct a loop to brute force the answer of (A) or is there a more pythonic way of answering these series of equations?

Linear algebra can only solve for multiples of your variables, not powers (that is why it is called linear, ie the equation for a straight line, Ax + By + Cz = 0).
For this set of equations you can use the quadratic formula to solve in terms of a:
x + 3 = a => x = a - 3
y * (y - 1) = a => y**2 - y - a = 0
y = (1 +/- (1 + 4*a) ** 0.5) / 2
= 0.5 +/- (0.25 + a) ** 0.5
(a >= -0.25 for real roots)
z * (4*z - 1) = a => 4 * z**2 - z - a = 0
z = (1 +/- (1 + 16*a) ** 0.5) / 8
= 0.125 +/- (0.015625 + 0.25*a) ** 0.5
(a >= -0.0625 for real roots)
then
def solve(a):
assert a >= -0.625, "No real solution"
x = a - 3
yoffs = (0.25 * a) ** 0.5
ylo = 0.5 - yoffs
yhi = 0.5 + yoffs
zoffs = (0.015625 + 0.25 * a) ** 0.5
zlo = 0.125 - zoffs
zhi = 0.125 + zoffs
return [
(x, ylo, zlo),
(x, ylo, zhi),
(x, yhi, zlo),
(x, yhi, zhi)
]

You do not have a system of 3 equations with 3 unknowns. You have a system of 3 equations with 4 unknowns: x, y, z and A.
That means your answer will be parameterized on A, because you do not have enough equations to solve for all unknowns.
Solving a general system of polynomial equations can be done by the so-called Groebner basis approach, which is what sympy uses. Here is a snippet on how to use the library to solve this or similar problems:
from sympy.solvers.polysys import solve_poly_system
from sympy.abc import x, y, z, A
f1 = x + 3 - A
f2 = y * (2 * y - 2) - 2 * A
f3 = z * (4 * z - 1) - A
solve_poly_system([f1, f2, f3], x, y, z)
# Outputs:
# [(A - 3, -sqrt(4*A + 1)/2 + 1/2, -sqrt(16*A + 1)/8 + 1/8),
# (A - 3, -sqrt(4*A + 1)/2 + 1/2, sqrt(16*A + 1)/8 + 1/8),
# (A - 3, sqrt(4*A + 1)/2 + 1/2, -sqrt(16*A + 1)/8 + 1/8),
# (A - 3, sqrt(4*A + 1)/2 + 1/2, sqrt(16*A + 1)/8 + 1/8)]
As you can see, the result requires to fix the value of A to be fully determined.

Related

Why does SymPy yield a "worse" solution than WolframAlpha?

I needed to solve $sin(x)+a*sin(3x)=0$ symbolically for $x$.
import sympy as sp
a,x = sp.symbols('a,x')
roots = sp.solve([sp.sin(x)+a*sp.sin(3*x)],[x])
print(roots)
produced
(I*(-log((-a - sqrt(-3*a**2 + 2*a + 1) - 1)/a) + log(2))/2,),
(I*(-log(-sqrt((-a + sqrt(-3*a**2 + 2*a + 1) - 1)/a)) + log(2)/2),),
(I*(-log((-a + sqrt(-3*a**2 + 2*a + 1) - 1)/a) + log(2))/2,)]
whereas WolframAlpha produced a much "better" solution:
solve sin(x) + a sin(3 x) = 0
yields
x = π n and n element Z
x = 2 π n and a = -1/3 and n element Z
x = 2 π n - 2 tan^(-1)(sqrt((5 a - 4 sqrt((a - 1) a) - 1)/(3 a + 1))) and 3 a + 1!=0 and sqrt((a - 1) a)!=2 a and n element Z
x = 2 (tan^(-1)(sqrt((5 a - 4 sqrt((a - 1) a) - 1)/(3 a + 1))) + π n) and 3 a + 1!=0 and sqrt((a - 1) a)!=2 a and n element Z
x = 2 π n - 2 tan^(-1)(sqrt((5 a + 4 sqrt((a - 1) a) - 1)/(3 a + 1))) and 3 a + 1!=0 and 2 a + sqrt((a - 1) a)!=0 and n element Z
Question:
can the SymPy solver be configured to produce solutions in the way WolframAlpha does?
Is anything known why SymPy expresses the solutions via (complex) logarithms instead of arc tangents?

python algorithm for solving systems of equations without

I need algorithm, that solve systems like this:
Example 1:
5x - 6y = 0 <--- line
(10- x)**2 + (10- y)**2 = 2 <--- circle
Solution:
find y:
(10- 6/5*y)**2 + (10- y)**2 = 2
100 - 24y + 1.44y**2 + 100 - 20y + y**2 = 2
2.44y**2 - 44y + 198 = 0
D = b**2 - 4ac
D = 44*44 - 4*2.44*198 = 3.52
y[1,2] = (-b+-sqrt(D))/2a
y[1,2] = (44+-1.8761)/4.88 = 9.4008 , 8.6319
find x:
(10- x)**2 + (10- 5/6y)**2 = 2
100 - 20x + y**2 + 100 - 5/6*20y + (5/6*y)**2 = 2
1.6944x**2 - 36.6666x + 198 = 0
D = b**2 - 4ac
D = 36.6666*36.6666 - 4*1.6944*198 = 2.4747
x[1,2] = (-b+-sqrt(D))/2a
x[1,2] = (36.6666+-1.5731)/3.3888 = 11.2841 , 10.3557
my skills are not enough to write this algorithm please help
and another algorithm that solve this system.
5x - 6y = 0 <--- line
|-10 - x| + |-10 - y| = 2 <--- rhomb
as answer here i need two x and two y.
You can use sympy, Python's symbolic math library.
Solutions for fixed parameters
from sympy import symbols, Eq, solve
x, y = symbols('x y', real=True)
eq1 = Eq(5 * x - 6 * y, 0)
eq2 = Eq((10 - x) ** 2 + (10 - y) ** 2, 2)
solutions = solve([eq1, eq2], (x, y))
print(solutions)
for x, y in solutions:
print(f'{x.evalf()}, {y.evalf()}')
This leads to two solutions:
[(660/61 - 6*sqrt(22)/61, 550/61 - 5*sqrt(22)/61),
(6*sqrt(22)/61 + 660/61, 5*sqrt(22)/61 + 550/61)]
10.3583197613288, 8.63193313444070
11.2810245009662, 9.40085375080520
The other equations work very similar:
eq1 = Eq(5 * x - 6 * y, 0)
eq2 = Eq(Abs(-10 - x) + Abs(-10 - y), 2)
leading to :
[(-12, -10),
(-108/11, -90/11)]
-12.0000000000000, -10.0000000000000
-9.81818181818182, -8.18181818181818
Dealing with arbitrary parameters
For your new question, how to deal with arbitrary parameters, sympy can help to find formulas, at least when the structure of the equations is fixed:
from sympy import symbols, Eq, Abs, solve
x, y = symbols('x y', real=True)
a, b, xc, yc = symbols('a b xc yc', real=True)
r = symbols('r', real=True, positive=True)
eq1 = Eq(a * x - b * y, 0)
eq2 = Eq((xc - x) ** 2 + (yc - y) ** 2, r ** 2)
solutions = solve([eq1, eq2], (x, y))
Studying the generated solutions, some complicated expressions are repeated. Those could be substituted by auxiliary variables. Note that this step isn't necessary, but helps a lot in making sense of the solutions. Also note that substitution in sympy often only considers quite literal replacements. That's by the introduction of c below is done in two steps:
c, d = symbols('c d', real=True)
for xi, yi in solutions:
print(xi.subs(a ** 2 + b ** 2, c)
.subs(r ** 2 * a ** 2 + r ** 2 * b ** 2, c * r ** 2)
.subs(-a ** 2 * xc ** 2 + 2 * a * b * xc * yc - b ** 2 * yc ** 2 + c * r ** 2, d)
.simplify())
print(yi.subs(a ** 2 + b ** 2, c)
.subs(r ** 2 * a ** 2 + r ** 2 * b ** 2, c * r ** 2)
.subs(-a ** 2 * xc ** 2 + 2 * a * b * xc * yc - b ** 2 * yc ** 2 + c * r ** 2, d)
.simplify())
Which gives the formulas:
x1 = b*(a*yc + b*xc - sqrt(d))/c
y1 = a*(a*yc + b*xc - sqrt(d))/c
x2 = b*(a*yc + b*xc + sqrt(d))/c
y2 = a*(a*yc + b*xc + sqrt(d))/c
These formulas then can be converted to regular Python code without the need of sympy. That code will only work for an arbitrary line and circle. Some tests need to be added around, such as c == 0 (meaning the line is just a dot), and d either be zero, positive or negative.
The stand-alone code could look like:
import math
def give_solutions(a, b, xc, yc, r):
# intersection between a line a*x-b*y==0 and a circle with center (xc, yc) and radius r
c =a ** 2 + b ** 2
if c == 0:
print("degenerate line equation given")
else:
d = -a**2 * xc**2 + 2*a*b * xc*yc - b**2 * yc**2 + c * r**2
if d < 0:
print("no solutions")
elif d == 0:
print("1 solution:")
print(f" x1 = {b*(a*yc + b*xc)/c}")
print(f" y1 = {a*(a*yc + b*xc)/c}")
else: # d > 0
print("2 solutions:")
sqrt_d = math.sqrt(d)
print(f" x1 = {b*(a*yc + b*xc - sqrt_d)/c}")
print(f" y1 = {a*(a*yc + b*xc - sqrt_d)/c}")
print(f" x2 = {b*(a*yc + b*xc + sqrt_d)/c}")
print(f" y2 = {a*(a*yc + b*xc + sqrt_d)/c}")
For the rhombus, sympy doesn't seem to be able to work well with abs in the equations. However, you could use equations for the 4 sides, and test whether the obtained intersections are inside the range of the rhombus. (The four sides would be obtained by replacing abs with either + or -, giving four combinations.)
Working this out further, is far beyond the reach of a typical stackoverflow answer, especially as you seem to ask for an even more general solution.

Equating differential equations in python

I want to equate those differential equations. I know I can solve them easily in the paper but I want to know how to do it in Python:
from sympy import symbols, Eq, solve
P = Function("P")
Q = Symbol('Q')
Q_d = Symbol("Q_d")
Q_s = Symbol("Q_s")
t = Symbol("t")
dy2 = 3 * Derivative(P(t), t,2)
dy1 = Derivative(P(t), t)
eq1 = Eq(dy2 + dy1 - P(t) + 9,Q_d)
display(eq1)
dy2_ = 5 * Derivative(P(t), t,2)
dy1_ = -Derivative(P(t), t)
eq2 = Eq(dy2_ + dy1_ +4* P(t) -1 ,Q_s)
display(eq2)
−𝑃(𝑡) + 𝑑/𝑑𝑡*𝑃(𝑡)+3*𝑑2/𝑑𝑡2 * 𝑃(𝑡) + 9 = 𝑄𝑑
4𝑃(𝑡) − 𝑑/𝑑𝑡*𝑃(𝑡)+5*𝑑2/𝑑𝑡2 * 𝑃(𝑡) −1 = 𝑄𝑠
These are basically "supply and demand" equations the result is basically:
2 * 𝑑2/𝑑𝑡2 * 𝑃(𝑡) = (2 * 𝑑/𝑑𝑡𝑃(𝑡) - 5𝑃(𝑡) +10)
How can I find this result? I know Sympy "Solve" can do such a thing:
solve((eq1,eq2), (x, y))
But in this case, I don't have any knowledge.
I assume you get what you call the result by setting Qs = Qd and subtracting the equations? It can be rewritten as
(2 * 𝑑/𝑑𝑡𝑃(𝑡) - 5𝑃(𝑡) +10) - 2 * 𝑑2/𝑑𝑡2 * 𝑃(𝑡) = 0
which you can obtain in sympy doing
>>> eq1.lhs - eq2.lhs
-5*P(t) + 2*Derivative(P(t), t) - 2*Derivative(P(t), (t, 2)) + 10
where lhs returns the left-hand side of the equation.

Dropping lower order terms of a polynomial with Sympy

Say we have this function,
f = poly(2*x**2 + 3*x - 1,x)
How would one go about dropping terms of degree n or lower.
For instance if n = 1 the result would be 2*x**2.
from sympy import poly
from sympy.abc import x
p = poly(x ** 5 + 2 * x ** 4 - x ** 3 - 2 * x ** 2 + x)
print(p)
n = 2
new_p = poly(sum(c * x ** i[0] for i, c in p.terms() if i[0] > n))
print(new_p)
Output:
Poly(x**5 + 2*x**4 - x**3 - 2*x**2 + x, x, domain='ZZ')
Poly(x**5 + 2*x**4 - x**3, x, domain='ZZ')

Matplotlib contour isn't working

I'm trying to plot the batman equation. A solution in sympy or matplotlib will be great (sage isn't cool because I'm using windows). The problem is that if I comment out certain parts the part of the figure appears but with all the F *= parts, I get a blank plot.
import matplotlib.pyplot
from numpy import arange
from numpy import meshgrid
from numpy import sqrt
from numpy import real
delta = 0.01
xrange = arange(-7.0, 7.0, delta)
yrange = arange(-3.0, 3.0, delta)
x, y = meshgrid(xrange,yrange)
F = 1
F *= (((x/7) ** 2) * sqrt(abs(abs(x) - 3)/(abs(x) - 3)) + ((y / 3) ** 2) * sqrt(abs(y + (3 * sqrt(33)) / 7)/(y + (3 * sqrt(33)) / 7)) - 1)
F *= (abs(x/2) - ((3 * sqrt(33) - 7)/112) * x**2 - 3 + sqrt(1 - (abs(abs(x) - 2) - 1) ** 2 ) - y)
F *= (9 * sqrt(abs((abs(x) - 1) * (abs(x) - 3/4))/((1 - abs(x)) * (abs(x) - 3/4))) - 8 * abs(x) - y)
F *= (3 * abs(x) + 0.75 * sqrt(abs((abs(x) - 3/4) * (abs(x) - 1/2))/((3/4 - abs(x)) * (abs(x) - 1/2))) - y)
F *= ((9/4) * sqrt(abs((x - 1/2) * (x + 1/2))/((1/2 - x) * (1/2 + x))) - y)
F *= ((6 * sqrt(10)) / 7 + (3/2 - abs(x)/2) * sqrt(abs(abs(x) - 1)/(abs(x) - 1)) - ((6 * sqrt(10))/ 14) * sqrt(4 - (abs(x) - 1) ** 2 ) - y)
G = 0
matplotlib.pyplot.contour(x, y, (F - G), [0])
matplotlib.pyplot.show()
What's going on here? If the graph is zero for one multiplicand, it should still be so no matter which other multiplicands I throw in there.
source of the batman equation: http://www.reddit.com/r/pics/comments/j2qjc/do_you_like_batman_do_you_like_math_my_math/
The parameter of sqrt is negative for many points, so the finally products are all NaN. You can plot every factor as following:
from __future__ import division # this is important, otherwise 1/2 will be 0
import matplotlib.pyplot
from numpy import arange
from numpy import meshgrid
from numpy import sqrt
from numpy import real
delta = 0.01
xrange = arange(-7.0, 7.0, delta)
yrange = arange(-3.0, 3.0, delta)
x, y = meshgrid(xrange,yrange)
F1 = (((x/7) ** 2) * sqrt(abs(abs(x) - 3)/(abs(x) - 3)) + ((y / 3) ** 2) * sqrt(abs(y + (3 * sqrt(33)) / 7)/(y + (3 * sqrt(33)) / 7)) - 1)
F2 = (abs(x/2) - ((3 * sqrt(33) - 7)/112) * x**2 - 3 + sqrt(1 - (abs(abs(x) - 2) - 1) ** 2 ) - y)
F3 = (9 * sqrt(abs((abs(x) - 1) * (abs(x) - 3/4))/((1 - abs(x)) * (abs(x) - 3/4))) - 8 * abs(x) - y)
F4 = (3 * abs(x) + 0.75 * sqrt(abs((abs(x) - 3/4) * (abs(x) - 1/2))/((3/4 - abs(x)) * (abs(x) - 1/2))) - y)
F5 = ((9/4) * sqrt(abs((x - 1/2) * (x + 1/2))/((1/2 - x) * (1/2 + x))) - y)
F6 = ((6 * sqrt(10)) / 7 + (3/2 - abs(x)/2) * sqrt(abs(abs(x) - 1)/(abs(x) - 1)) - ((6 * sqrt(10))/ 14) * sqrt(4 - (abs(x) - 1) ** 2 ) - y)
for f in [F1,F2,F3,F4,F5,F6]:
matplotlib.pyplot.contour(x, y, f, [0])
matplotlib.pyplot.show()
the result plot:
I know this might seem lame, but how about creating a list of x values, and then computing the value of "batman" at each of those positions, and storing in another list. You could define a function "batman" which computes the y value for each x value you pass in.
Then just plot those lists with matplotlib.
EDIT: Since you've made numpy arrays already to store the results, you could use those when computing the y values.
I'm not even sure how this equation would work, since I see divisions by zero arising in the first term (under the first square root, when abs(x) = 3), and imaginary numbers showing up in the last term (under the last square root, when {abs(x)-1}^2 > 4, ie x > 3 or x < -3).
What am I missing here? Is only the real part of the result used, and are divisions by zero ignored or approximated?
Running this, I do indeed see lots of RunTimeWarnings, and it is not unlikely matplotlib would get totally confused what numbers to work with (NaNs, Infs; trying print F at the end). Looks like it still manages when there's only a relatively low number of NaNs or Infs, which would explain that you're seeing part of the figure.
I'd think matplotlib's contour is fine, just confused by the input.

Categories

Resources