How to expand matrix expressions in SymPy? - python

In SymPy, I am trying to perform a matrix multiplication and expand it afterwards. However, SymPy does not seem to support the expansion of matrix expressions. For example, here is the 4th order Runge-Kutta (RK4) for matrices:
from sympy import init_session
init_session()
from sympy import *
A = MatrixSymbol('A', 3, 3)
x = MatrixSymbol('x', 3, 1)
dt = symbols('dt')
k1 = A*x
k2 = A*(x + S(1)/2*k1*dt)
k3 = A*(x + S(1)/2*k2*dt)
k4 = A*(x + k3*dt)
final = dt*S(1)/6*(k1 + 2*k2 + 2*k3 + k4)
final.expand()
which produces the result
Traceback (most recent call last)
<ipython-input-38-b3ff67883c61> in <module>()
12 final = dt*1/6*(k1+2*k2+2*k3+k4)
13
---> 14 final.expand()
AttributeError: 'MatMul' object has no attribute 'expand'
I hope the expression can be expanded just like the scalar variant:
A,x,dt = symbols('A x dt')
k1 = A*x
k2 = A*(x+k1*dt*S(1)/2)
k3 = A*(x+k2*dt*S(1)/2)
k4 = A*(x+k3*dt)
final = x+dt*(S(1)/6)*(k1+k2+k3+k4)
collect(expand((final)),x)
with the result:
x*(A**4*dt**4/24 + A**3*dt**3/8 + A**2*dt**2/3 + 2*A*dt/3 + 1)
Is it possible to alter a matrix expression likewise?
nicoguaro's answer takes the error away, but expands the the whole expression into one matrix. As illustrated with the scalar example, not what I'm looking for.

Matrix(final) creates and explicit Matrix with your individual equations. It may be convenient to leave them in the matrix so that whatever you do to one entry can be done to all. To do so, define the manipulation that you want to do as a function argument for applyfunc:
>>> ex = Matrix(final)
>>> ex = ex.applyfunc(expand)
>>> ex = ex.applyfunc(lambda i: collect(i, dt))
...
For printing economy I use Matrices with compact symbol entries to compute the same and then run cse over the simplified matrix to give:
>>> A = Matrix(3, 3, var('a:3:3'))
>>> x = Matrix(3, 1, var('x:3'))
>>> dt = symbols('dt')
>>> k1 = A*x
>>> k2 = A*(x + S(1)/2*k1*dt)
>>> k3 = A*(x + S(1)/2*k2*dt)
>>> k4 = A*(x + k3*dt)
>>> final = dt*S(1)/6*(k1 + 2*k2 + 2*k3 + k4)
>>> eqi = Matrix(final)
>>> print(cse(eqi.applyfunc(simplify)))
([(x3, 4*x0), (x4, a01*x1), (x5, a02*x2), (x6, dt*(a00*x0 + x4 + x5) +
2*x0), (x7, a00*x6), (x8, a11*x1), (x9, a12*x2), (x10, dt*(a10*x0 + x8
+ x9) + 2*x1), (x11, a01*x10), (x12, a21*x1), (x13, a22*x2), (x14,
dt*(a20*x0 + x12 + x13) + 2*x2), (x15, a02*x14), (x16, dt*(x11 + x15 +
x7) + x3), (x17, a00*x16), (x18, 4*x1), (x19, a10*x6), (x20, a11*x10),
(x21, a12*x14), (x22, dt*(x19 + x20 + x21) + x18), (x23, a01*x22),
(x24, 4*x2), (x25, a20*x6), (x26, a21*x10), (x27, a22*x14), (x28,
dt*(x25 + x26 + x27) + x24), (x29, a02*x28), (x30, dt*(x17 + x23 +
x29) + x3), (x31, a10*x16), (x32, a11*x22), (x33, a12*x28), (x34,
dt*(x31 + x32 + x33) + x18), (x35, a20*x16), (x36, a21*x22), (x37,
a22*x28), (x38, dt*(x35 + x36 + x37) + x24), (x39, dt/24)], [Matrix([
[ x39*(a00*x3 + a00*x30 + a01*x34 + a02*x38 + 4*x11 + 4*x15 + 2*x17
+ 2*x23 + 2*x29 + 4*x4 + 4*x5 + 4*x7)], [ x39*(a10*x3 + a10*x30 +
a11*x34 + a12*x38 + 4*x19 + 4*x20 + 4*x21 + 2*x31 + 2*x32 + 2*x33 +
4*x8 + 4*x9)], [x39*(a20*x3 + a20*x30 + a21*x34 + a22*x38 + 4*x12 +
4*x13 + 4*x25 + 4*x26 + 4*x27 + 2*x35 + 2*x36 + 2*x37)]])])
Limited support of noncommutative expressions is also available and can help in this situation:
>>> A, x = symbols("A x", commutative=False)
>>> dt = symbols('dt')
>>> k1 = A*x
>>> k2 = A*(x + S(1)/2*k1*dt)
>>> k3 = A*(x + S(1)/2*k2*dt)
>>> k4 = A*(x + k3*dt)
>>> final = dt*S(1)/6*(k1 + 2*k2 + 2*k3 + k4)
>>> final.expand()
dt**4*A**4*x/24 + dt**3*A**3*x/6 + dt**2*A**2*x/2 + dt*A*x
>>> factor(_)
dt*A*(dt**3*A**3/24 + dt**2*A**2/6 + dt*A/2 + 1)*x
But not all simplification routines are nc aware (and this is a known issue):
>>> collect(final,x)
Traceback (most recent call last):
...
AttributeError: Can not collect noncommutative symbol

I think that you can expand Matrix expressions. But what you have is not a matrix but the multiplication of two symbolic matrices (Matsymbols). If you turn your expression to a matrix you can get the expansion you wanted. See the extra line below
from sympy import init_session
init_session()
from sympy import *
A = MatrixSymbol('A', 3, 3)
x = MatrixSymbol('x', 3, 1)
dt = symbols('dt')
k1 = A*x
k2 = A*(x + S(1)/2*k1*dt)
k3 = A*(x + S(1)/2*k2*dt)
k4 = A*(x + k3*dt)
final = dt*S(1)/6*(k1 + k2 + k3 + k4)
Matrix(final).expand()

Related

Using numpy broadcasting to compute the partial sum of a series

I have to compute the following sum, S defined as:
I have tried the following as a function:
import numpy as np
a = np.array([0.4,2.6,3, 1.2, 3.4])
b = np.array([-5,7,2,1.1,1.8])
c = np.array([3.3,30,15,0.4,28])
t = np.linspace(0, np.pi, 600)
def S(t):
return np.sum([a[i]*np.cos(b[i]*t + c[i]) for i in range(5)], axis=0)
This works just fine. But I wonder if there is pure numpy version that uses broadcasting without relying on Python's list comprehension?
I have tried:
def S(t):
return np.sum(a*np.cos(b*t + c), axis=0)
When I compue S(t) I get the following error:
...
ValueError: operands could not be broadcast together with shapes (5,) (600,)
How can I get this to work properly?
How to do it?
For broadcasting you need ones in dimensions you want to have "copied" values. So it works like this:
import numpy as np
a = np.array([0.4,2.6,3, 1.2, 3.4])
b = np.array([-5,7,2,1.1,1.8])
c = np.array([3.3,30,15,0.4,28])
t = np.linspace(0, np.pi, 600).reshape(-1,1)
np.sum(a*np.cos(b*t+c), axis=1)
How to check if you did the right thing?
One neat hack you can do is to put sympy.symbols into your arrays and do the same thing. Then you obtain formulas for what it calculated for you:
import sympy
a = np.array(sympy.symbols([f"a{i}" for i in range(5)]))
b = np.array(sympy.symbols([f"b{i}" for i in range(5)]))
c = np.array(sympy.symbols([f"c{i}" for i in range(5)]))
t = np.array(sympy.symbols([f"t{i}" for i in range(10)])).reshape(-1,1)
cos = np.vectorize(sympy.cos)
np.sum(a*cos(b*t+c), axis=1)
this gives you
array([a0*cos(b0*t0 + c0) + a1*cos(b1*t0 + c1) + a2*cos(b2*t0 + c2) + a3*cos(b3*t0 + c3) + a4*cos(b4*t0 + c4),
a0*cos(b0*t1 + c0) + a1*cos(b1*t1 + c1) + a2*cos(b2*t1 + c2) + a3*cos(b3*t1 + c3) + a4*cos(b4*t1 + c4),
a0*cos(b0*t2 + c0) + a1*cos(b1*t2 + c1) + a2*cos(b2*t2 + c2) + a3*cos(b3*t2 + c3) + a4*cos(b4*t2 + c4),
a0*cos(b0*t3 + c0) + a1*cos(b1*t3 + c1) + a2*cos(b2*t3 + c2) + a3*cos(b3*t3 + c3) + a4*cos(b4*t3 + c4),
a0*cos(b0*t4 + c0) + a1*cos(b1*t4 + c1) + a2*cos(b2*t4 + c2) + a3*cos(b3*t4 + c3) + a4*cos(b4*t4 + c4),
a0*cos(b0*t5 + c0) + a1*cos(b1*t5 + c1) + a2*cos(b2*t5 + c2) + a3*cos(b3*t5 + c3) + a4*cos(b4*t5 + c4),
a0*cos(b0*t6 + c0) + a1*cos(b1*t6 + c1) + a2*cos(b2*t6 + c2) + a3*cos(b3*t6 + c3) + a4*cos(b4*t6 + c4),
a0*cos(b0*t7 + c0) + a1*cos(b1*t7 + c1) + a2*cos(b2*t7 + c2) + a3*cos(b3*t7 + c3) + a4*cos(b4*t7 + c4),
a0*cos(b0*t8 + c0) + a1*cos(b1*t8 + c1) + a2*cos(b2*t8 + c2) + a3*cos(b3*t8 + c3) + a4*cos(b4*t8 + c4),
a0*cos(b0*t9 + c0) + a1*cos(b1*t9 + c1) + a2*cos(b2*t9 + c2) + a3*cos(b3*t9 + c3) + a4*cos(b4*t9 + c4)],
dtype=object)
Notice that this couldn't work with numpy's cosine since that only works for floats. But
cos = np.vectorize(sympy.cos)
gives you a version of sympys cosine that works on arrays elementwise.
I think I found the answer by adding a newaxis to a, b, and c arrays as follows:
def S(t):
return np.sum(a[:,None]*cos(b[:,None]*t + c[:,None]), axis=0)
or
def S(t):
return np.sum(a[:,np.newaxis]*cos(b[:,np.newaxis]*t + c[:,np.newaxis]), axis=0)
Edit:
As suggested by Michael in the comment, a better solution, would be to add a newaxis to t instead (note that in this case the sum is carried out over axis=1):
def S(t):
return np.sum(a*cos(b*t[:,None] + c), axis=1)

sympy: system of equations for double pendulum

I want to solve system of equation described in the article Double Pendulum, similar like author, where in last step he claims that he "use a computer algebra program to solve equations (13) and (16)". To do this, I rewrite this equations in SymPy:
import sympy as s
m1, m2 = s.symbols('m1, m2')
g = s.symbols('g')
x1_d2, y1_d2, x2_d2, y2_d2 = s.symbols('x1_d2, y1_d2, x2_d2, y2_d2')
t1, t2, t1_d1, t1_d2, t2_d1, t2_d2 = s.symbols('t1, t2, t1_d1, t1_d2, t2_d1, t2_d2')
L1, L2 = s.symbols('L1, L2')
eq1 = x1_d2 + t1_d1**2 * L1 * s.sin(t1) - t1_d2 * L1 * s.cos(t1)
eq2 = y1_d2 - t1_d1**2 * L1 * s.cos(t1) - t1_d2 * L1 * s.sin(t1)
eq3 = x2_d2 - x1_d2 + t2_d1**2 * L2 * s.sin(t2) - t2_d2 * L2 * s.cos(t2)
eq4 = y2_d2 - y1_d2 - t2_d1**2 * L2 * s.cos(t2) - t2_d2 * L2 * s.sin(t2)
eq16 = s.sin(t2) * (m2 * y2_d2 + m2 * g) + s.cos(t2) * (m2 * x2_d2)
result1 = s.solve([eq1, eq2, eq3, eq4, eq16], \
[m1, m2, g, x1_d2, y1_d2, x2_d2, y2_d2, t1, t2, t1_d1, t1_d2, t2_d1, t2_d2, L1, L2], \
dict=True)
for r in result1:
print(r.keys())
eq13 = s.sin(t1) * (m1 * y1_d2 + m2 * y2_d2 + m2 * g + m1 * g) + s.cos(t1) * (m1 * x1_d2 + m2 * x2_d2)
result2 = s.solve([eq1, eq2, eq3, eq4, eq13], \
[m1, m2, g, x1_d2, y1_d2, x2_d2, y2_d2, t1, t2, t1_d1, t1_d2, t2_d1, t2_d2, L1, L2], \
dict=True)
for r in result2:
print(r.keys())
Looking on similar question like How can I solve system of linear equations in SymPy?
, I expected that SymPy simplify, and return equations in respect to each symbol, however for equation (16) I got the result only for: L1, g, t1, L2, t2, and not for y2_d2 t2_d2. And for equation (13) I got exception.
dict_keys([L1, g, t1, L2, t2])
dict_keys([L1, g, t1, L2, t2])
Traceback (most recent call last):
File "physics-simulations/formula.py", line 28, in <module>
dict=True)
File "/usr/lib/python3/dist-packages/sympy/solvers/solvers.py", line 1164, in solve
solution = _solve_system(f, symbols, **flags)
File "/usr/lib/python3/dist-packages/sympy/solvers/solvers.py", line 1911, in _solve_system
soln = _solve(eq2, s, **flags)
File "/usr/lib/python3/dist-packages/sympy/solvers/solvers.py", line 1752, in _solve
result = [r for r in result if
File "/usr/lib/python3/dist-packages/sympy/solvers/solvers.py", line 1753, in <listcomp>
checksol(f_num, {symbol: r}, **flags) is not False]
File "/usr/lib/python3/dist-packages/sympy/solvers/solvers.py", line 355, in checksol
return bool(abs(val.n(18).n(12, chop=True)) < 1e-9)
File "/usr/lib/python3/dist-packages/sympy/core/expr.py", line 336, in __lt__
raise TypeError("Invalid NaN comparison")
TypeError: Invalid NaN comparison
Edited:
How should my code look like, to solve/find two unknowns y1_d2, y2_d2 t1_d1, t2_d2 - equations (13, 16)?
This sort of manipulation is described here; using the focus routine described there you can feed your full equation set (cast as Eq instances) to focus and specify y1_d2 and y2_d2 as the variables on which you want to focus:
>>> F = (y1_d2, y2_d2)
>>> f = focus([Eq(i,0) for i in [eq1, eq2, eq3, eq4, eq16, eq13]], *F)
>>> [f[i].has(*F) for i in F]
[False, False]
>>> count_ops(f)
323
A compact result can be obtained with cse:
>>> r, e =cse([Eq(*i) for i in f.items()])
>>> for i in r:
... print('%s = %s' % i)
...
x0 = sin(t2)
x1 = cos(t2)
x2 = t2_d1**2
x3 = 1/(-t2_d2*x1 + x0*x2)
x4 = t2_d2*x0*x3
x5 = sin(t1)
x6 = cos(t1)
x7 = t1_d1**2
x8 = 1/(-t1_d2*x6 + x5*x7)
x9 = t1_d2*x5*x8
x10 = x1*x2*x3
x11 = x6*x7*x8
x12 = g/(x10 - x11 + x4 - x9)
x13 = x11*x12 + x12*x9
x14 = -x12 - x2_d2
>>> e
[Eq(y1_d2, x13), Eq(y2_d2, x10*x14 + x13 + x14*x4)]
I think the routines will only work with linear variables so if you want to solve for t1_d1 it will fail. But since t1_d1 only appears as a square you can replace it with y and focus on y. So here is a way to focus on t1_d1 and t2_d2:
>>> eqs = Tuple(*[Eq(i,0) for i in [eq1, eq2, eq3, eq4, eq16, eq13])
>>> S(focus(eqs.subs(t1_d1**2, y), y, t2_d2)).subs(y, t1_d1**2)
{t1_d1**2: (-L1*t1_d2*sin(t1) + y1_d2)/(L1*cos(t1)),
t2_d2: (-L2*t2_d1**2*cos(t2) - y1_d2 + y2_d2)/(L2*sin(t2))}
Thanks to smichr clue I was able to get desired result.
from sympy import symbols, sin, cos, solve, simplify, Eq
m1, m2 = symbols('m1, m2')
g = symbols('g')
t1, t2, t1_d1, t1_d2, t2_d1, t2_d2 = symbols('t1, t2, t1_d1, t1_d2, t2_d1, t2_d2')
L1, L2 = symbols('L1, L2')
x1_d2 = -t1_d1**2*L1*sin(t1) + t1_d2*L1*cos(t1)
y1_d2 = t1_d1**2*L1*cos(t1) + t1_d2*L1*sin(t1)
x2_d2 = x1_d2 - t2_d1**2*L2*sin(t2) + t2_d2*L2*cos(t2)
y2_d2 = y1_d2 + t2_d1**2*L2*cos(t2) + t2_d2*L2*sin(t2)
eq13 = Eq(sin(t1)*(m1*y1_d2 + m2*y2_d2 + m2*g + m1*g) + cos(t1)*(m1*x1_d2 + m2*x2_d2), 0)
eq16 = Eq(sin(t2)*(m2*y2_d2 + m2*g) + cos(t2)*(m2*x2_d2), 0)
solve([eq13, eq16], [t1_d1, t2_d2], dict=True)
Result:
[{t1_d1: -sqrt((L1*m2*t1_d2*sin(2*t1 - 2*t2) - (2*L1*m1*t1_d2 + L1*m2*t1_d2 + 2*L2*m2*t2_d1**2*sin(t1 - t2) + 2*g*m1*sin(t1) + g*m2*sin(t1) + g*m2*sin(t1 - 2*t2))*tan(2*t1 - 2*t2))/(L1*m2*sin(2*t1 - 2*t2)*tan(2*t1 - 2*t2))),
t2_d2: -(L1*m1*t1_d2 + L1*m2*t1_d2 + L2*m2*t2_d1**2*sin(t1 - t2) + g*m1*sin(t1) + g*m2*sin(t1))/(L2*m2*cos(t1 - t2))},
{t1_d1: sqrt((L1*m2*t1_d2*sin(2*t1 - 2*t2) - (2*L1*m1*t1_d2 + L1*m2*t1_d2 + 2*L2*m2*t2_d1**2*sin(t1 - t2) + 2*g*m1*sin(t1) + g*m2*sin(t1) + g*m2*sin(t1 - 2*t2))*tan(2*t1 - 2*t2))/(L1*m2*sin(2*t1 - 2*t2)*tan(2*t1 - 2*t2))),
t2_d2: -(L1*m1*t1_d2 + L1*m2*t1_d2 + L2*m2*t2_d1**2*sin(t1 - t2) + g*m1*sin(t1) + g*m2*sin(t1))/(L2*m2*cos(t1 - t2))}]

how to solve a set of expression

how to solve a set of expressions in Python, not numerical calcualtion but purely analytic way?
I checked several functions of sympy which seems unable to do the work.
Assuming we are going to find the expression for x1,x2,x3 and x4 with following equations:
x1 = a*s+b*x2 + c
x2 = (m+n) * x3 + d
x3 = l*s + k*x4 + e
x4 = i*s + j*x1 + f
where x1,x2,x3,x4,a,b,c,d,e,f,i,j,k,l,m,n are all symbols.
How to reach the result for such problems in Python.
First get the "paper" equations into SymPy Equalities:
>>> from sympy.utilities.iterables import reshape
>>> eqs=reshape(S('''(
x1 = a*s+b*x2 + c
x2 = (m+n) * x3 + d
x3 = l*s + k*x4 + e
x4 = i*s + j*x1 + f)'''.strip().replace(
'\n',',').replace('=',',')), (2,))
>>> eqs = [Eq(*i) for i in eqs]
Then solve the equations (see solve docstring):
>>> solve(eqs, var('x1:5'))
{x4: (a*s + b*d + b*(e + l*s)*(m + n) + c - (-f - i*s)/j)/(-b*k*(m + n) + 1/j),
x3: e + k*(a*s + b*d + b*(e + l*s)*(m + n) + c - (-f - i*s)/j)/(-b*k*(m+ n) + 1/j) +
l*s,
x1: (-f - i*s)/j + (a*s + b*d + b*(e + l*s)*(m + n) + c - (-f - i*s)/j)/(j*(-b*k*(m
+ n) + 1/j)),
x2: d + (m + n)*(e + k*(a*s + b*d + b*(e + l*s)*(m + n) + c - (-f -
i*s)/j)/(-b*k*(m + n) + 1/j) + l*s)}
As noted here, however, this symbolic solution is not valid for all values...it is only valid if none of the coefficients in front of the symbols for which you are solving are zero.

Solving an Equation System in SciPy

i am trying to fit an exponential curve through three given Points. But i get only very wrong results of fsolve or actual 0. I need this for my Bachelor Thesis so if anyone knows a better solution for the problem, it would be very kind to tell me this solution.
from numpy import *
from scipy.optimize import *
def myFunction(variables):
x1 = 1
y1 = 100
x2 = 5
y2 = 50
x3 = 10
y3 = 1
(a,k,b) = variables
y1 = a*exp(-x1*k)+b
y2 = a*exp(-x2*k)+b
y3 = a*exp(-x3*k)+b
#0 = a*k**2 * exp(-x1+k)
return ([a, k, b])
z = fsolve(myFunction,(1,0.1,5))
print(z)
this is my problem, i need to fit an e function through this 3 given points, and in addition the second derivation of the forumla should be 0
edit: 06.12.17
in some way i have now an improvement with a polynom, but does not really fit like it should.
The second Maximum should not be there.. :D
from numpy import *
from scipy.optimize import *
import matplotlib.pyplot as plt
def myFunction(z):
a = z[0]
b = z[1]
c = z[2]
d = z[3]
e = z[4]
f = z[5]
g = z[6]
x = [0, 10 ,15 ,20 ,50 ,100]
y = [10 ,90 ,100 ,90 ,50 ,10]
s = [0, 10, 1, 0, 0, 0]
F = empty((8))
F[0] = a*x[0]**6 + b*x[0]**5 + c*x[0]**4 + d*x[0]**3 + e*x[0]**2 + f*x[0]**1 + g - y[0]
F[1] = a*x[1]**6 + b*x[1]**5 + c*x[1]**4 + d*x[1]**3 + e*x[1]**2 + f*x[1]**1 + g - y[1]
F[2] = a*x[2]**6 + b*x[2]**5 + c*x[2]**4 + d*x[2]**3 + e*x[2]**2 + f*x[2]**1 + g - y[2]
F[3] = a*x[3]**6 + b*x[3]**5 + c*x[3]**4 + d*x[3]**3 + e*x[3]**2 + f*x[3]**1 + g - y[3]
F[4] = a*x[4]**6 + b*x[4]**5 + c*x[4]**4 + d*x[4]**3 + e*x[4]**2 + f*x[4]**1 + g - y[4]
F[5] = a*x[5]**6 + b*x[5]**5 + c*x[5]**4 + d*x[5]**3 + e*x[5]**2 + f*x[5]**1 + g - y[5]
F[6] = 6*a*x[3]**5 + 5*b*x[3]**4 + 4*c*x[3]**3 + 3*d*x[3]**2 + 2*e*x[3]**1 + f - s[3]
F[7] = 6*a*x[5]**5 + 5*b*x[5]**4 + 4*c*x[5]**3 + 3*d*x[5]**2 + 2*e*x[5]**1 + f - s[5]
return F
zGuess = array([1,1,1,1,1,1,1,1])
z = fsolve(myFunction,zGuess)
print(z)
x_axis = linspace(0,100,100)
y_axis = z[0]*x_axis**6 + z[1]*x_axis**5 + z[2]*x_axis**4 + z[3]*x_axis**3 + z[4]*x_axis**2 + z[5]*x_axis**1 + z[6]
plt.plot(x_axis, y_axis)
plt.show()
edit 07.12.17
the whole signal should look like the data of the second example. But the difficulty is in the part of the first example. My suggestion was to use 2 polynoms, but my prof would prefer an polynom x<20 and an e function x>20. The overlapping of both should also be very smooth.
Well fsolve find the roots of a function, does not really do a non-linear fit. I must admit I don't actually quite get what you want to achieve with your code. If you want to do a nonlinear fit (since you are talking about exponential functions here) you may want to check my notebook here https://github.com/michelucci/Regression-with-Python/blob/master/(Non)%20linear%20fit%20in%20Python.ipynb that I hope can point you in the right direction. It contains first a part on linear regression and then a non-linear tutorial.
You can check curve_fit() python function in the scipy.optimize library. That should help you with what you want to do.
Let me know if that helps you.
You may also want to check this link to better understand what a non-linear fit is https://en.wikipedia.org/wiki/Nonlinear_regression
Best, Umberto

Draw ellipse in matplotlib given the focii

The equation of an ellipse is:
sqrt((x-a1)**2 + (y-b1)**2) + np.sqrt((x-a2)**2 + (y-b2)**2) = c
The focii are (a1, b1) and (a2, b2). c is also known. How do I draw this in python using matplotlib?
Thanks for your help.
You can represent the ellipse parametrically in some variable t. You could look into Wikipedia to see how this can be done, for instance.
In the following code, I've derived the parameters necessary for the parametric form from the parameters that you've supplied.
# Example focii and sum-distance
a1 = 1
b1 = 2
a2 = 5
b2 = 7
c = 9
# Compute ellipse parameters
a = c / 2 # Semimajor axis
x0 = (a1 + a2) / 2 # Center x-value
y0 = (b1 + b2) / 2 # Center y-value
f = np.sqrt((a1 - x0)**2 + (b1 - y0)**2) # Distance from center to focus
b = np.sqrt(a**2 - f**2) # Semiminor axis
phi = np.arctan2((b2 - b1), (a2 - a1)) # Angle betw major axis and x-axis
# Parametric plot in t
resolution = 1000
t = np.linspace(0, 2*np.pi, resolution)
x = x0 + a * np.cos(t) * np.cos(phi) - b * np.sin(t) * np.sin(phi)
y = y0 + a * np.cos(t) * np.sin(phi) + b * np.sin(t) * np.cos(phi)
# Plot ellipse
plt.plot(x, y)
# Show focii
plt.plot(a1, b1, 'bo')
plt.plot(a2, b2, 'bo')
plt.axis('equal')
plt.show()
This gives what you need:
you need 2 lists or arrays of X, Y such that the elements satisfy the ellipse equation
the usual ellipse plotting solutions parameterize the ellipse equation by central (or focal) angle to make the X,Y functions single valued for the angle in 0 to 2pi
I showed a solution in Drawing elliptical orbit in Python (using numpy, matplotlib)
Y as a function of X with a hack to "feel out" the xrange, then piece together the dual Y solutions for each x
just the minimum mods to that code to put in your equation, would fail for a1 = a2
the symbolic solution takes a minute or so runtime
import numpy as np
import matplotlib.pyplot as plt
from sympy import *
# sqrt((x-a1)**2 + (y-b1)**2) + np.sqrt((x-a2)**2 + (y-b2)**2) = c
coeffs = [1, 0, -1, 0, 4]
xs = [coeffs[0], coeffs[2]]
def ysolv(coeffs):
x,y,a1,b1,a2,b2,c = symbols('x y a1 b1 a2 b2 c', real = True)
ellipse = sqrt((x-a1)**2 + (y-b1)**2) + sqrt((x-a2)**2 + (y-b2)**2) - c
y_sols = solve(ellipse, y)
print(*y_sols, sep='\n')
num_coefs = [(a, f) for a, f in (zip([a1,b1,a2,b2,c], coeffs))]
y_solsf0 = y_sols[0].subs(num_coefs)
y_solsf1 = y_sols[1].subs(num_coefs)
print(y_solsf0, '\n', y_solsf1)
f0 = lambdify([x], y_solsf0)
f1 = lambdify([x], y_solsf1)
return f0, f1
f0, f1 = ysolv(coeffs)
y0 = [f0(x) for x in xs]
y1 = [f1(x) for x in xs]
def feeloutXrange(f, midx, endx):
fxs = []
x = midx
while True:
try: f(x)
except:
break
fxs.append(x)
x += (endx - midx)/200
return fxs
midx = (min(xs) + max(xs))/2
xpos = feeloutXrange(f0, midx, max(xs))
xnegs = feeloutXrange(f0, midx, min(xs))
xs_ellipse = xnegs[::-1] + xpos[1:]
y0s = [f0(x) for x in xs_ellipse]
y1s = [f1(x) for x in xs_ellipse]
ys_ellipse = y0s + y1s[::-1] + [y0s[0]] # add y start point to end to close drawing
xs_ellipse = xs_ellipse + xs_ellipse[::-1] + [xs_ellipse[0]] # added x start point
plt.plot(xs_ellipse, ys_ellipse)
plt.show()
(-c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*(a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2 - c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 + b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x + a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 + b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*(b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2))
(c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*(a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2 - c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 + b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x + a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 + b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*(b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2))
sqrt(-48*x**2 + 192)/8
-sqrt(-48*x**2 + 192)/8
the other answers used the parametric transform approach
I especially like showing sympy solving the equation for you rather than someone hand solving it
the symbolic expression only needs be found once for a particular Ellipse parameterization, then the symbolic expression can simply be hard coded:
"""
for Ellipse equation:
sqrt((x-a1)**2 + (y-b1)**2) + sqrt((x-a2)**2 + (y-b2)**2) = c
sympy solution to Ellipse equation, only have to run once to get y_sols
symbolic expression to paste into ysolv below
#def symEllipse():
# x,y,a1,b1,a2,b2,c = symbols('x y a1 b1 a2 b2 c', real = True)
# ellipse = sqrt((x-a1)**2 + (y-b1)**2) + sqrt((x-a2)**2 + (y-b2)**2) - c
# y_sols = solve(ellipse, y)
# print(*y_sols, sep='\n')
"""
coeffs = [1, 1, -1, -1, 3]
xs = [coeffs[0], coeffs[2]]
def ysolv(coeffs):
x,y,a1,b1,a2,b2,c = symbols('x y a1 b1 a2 b2 c', real = True)
y_sols = [
(-c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*
(a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2
- c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 +
b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x +
a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 +
b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*
(b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2)),
(c*sqrt((a1**2 - 2*a1*a2 + a2**2 + b1**2 - 2*b1*b2 + b2**2 - c**2)*
(a1**2 + 2*a1*a2 - 4*a1*x + a2**2 - 4*a2*x + b1**2 - 2*b1*b2 + b2**2
- c**2 + 4*x**2))*(-b1 + b2 + c)*(b1 - b2 + c) + (b1**2 - 2*b1*b2 +
b2**2 - c**2)*(-a1**2*b1 + a1**2*b2 + 2*a1*b1*x - 2*a1*b2*x +
a2**2*b1 - a2**2*b2 - 2*a2*b1*x + 2*a2*b2*x - b1**3 + b1**2*b2 +
b1*b2**2 + b1*c**2 - b2**3 + b2*c**2))/(2*(-b1 + b2 + c)*
(b1 - b2 + c)*(b1**2 - 2*b1*b2 + b2**2 - c**2))
]
num_coefs = [(a, f) for a, f in (zip([a1,b1,a2,b2,c], coeffs))]
y_solsf0 = y_sols[0].subs(num_coefs)
y_solsf1 = y_sols[1].subs(num_coefs)
print(y_solsf0, '\n', y_solsf1)
f0 = lambdify([x], y_solsf0)
f1 = lambdify([x], y_solsf1)
return f0, f1

Categories

Resources