Найти общую касательную между двумя кубическими кривыми
Учитывая две функции, я хотел бы отсортировать общую касательную для обеих кривых:
Наклон общей касательной может быть получен следующим образом:
slope of common tangent = (f(x1) - g(x2)) / (x1 - x2) = f'(x1) = g'(x2)
Так что в итоге мы имеем систему из 2 уравнений с 2 неизвестными:
f'(x1) = g'(x2) # Eq. 1
(f(x1) - g(x2)) / (x1 - x2) = f'(x1) # Eq. 2
Почему-то я не понимаю, python не находит решения:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
import sys
from sympy import *
import sympy as sym
# Intial candidates for fit
E0_init = -941.510817926696
V0_init = 63.54960592453
B0_init = 76.3746233515232
B0_prime_init = 4.05340727164527
# Data 1 (Red triangles):
V_C_I, E_C_I = np.loadtxt('./1.dat', skiprows = 1).T
# Data 14 (Empty grey triangles):
V_14, E_14 = np.loadtxt('./2.dat', skiprows = 1).T
def BM(x, a, b, c, d):
return (2.293710449E+17)*(1E-21)* (a + b*x + c*x**2 + d*x**3 )
def P(x, b, c, d):
return -b - 2*c*x - 3 *d*x**2
init_vals = [E0_init, V0_init, B0_init, B0_prime_init]
popt_C_I, pcov_C_I = curve_fit(BM, V_C_I, E_C_I, p0=init_vals)
popt_14, pcov_14 = curve_fit(BM, V_14, E_14, p0=init_vals)
x1 = var('x1')
x2 = var('x2')
E1 = P(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3]) - P(x2, popt_14[1], popt_14[2], popt_14[3])
print 'E1 = ', E1
E2 = ((BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - BM(x2, popt_14[0], popt_14[1], popt_14[2], popt_14[3])) / (x1 - x2)) - P(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
sols = solve([E1, E2], [x1, x2])
print 'sols = ', sols
# Linspace for plotting the fitting curves:
V_C_I_lin = np.linspace(V_C_I[0], V_C_I[-1], 10000)
V_14_lin = np.linspace(V_14[0], V_14[-1], 10000)
plt.figure()
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, BM(V_C_I_lin, *popt_C_I), color='black', label='Cubic fit data 1' )
p6, = plt.plot(V_14_lin, BM(V_14_lin, *popt_14), 'b', label='Cubic fit data 2')
# Plotting the scattered points:
p1 = plt.scatter(V_C_I, E_C_I, color='red', marker="^", label='Data 1', s=100)
p5 = plt.scatter(V_14, E_14, color='grey', marker="^", facecolors='none', label='Data 2', s=100)
plt.ticklabel_format(useOffset=False)
plt.show()
1.dat
является следующим:
61.6634100000000 -941.2375622594436
62.3429030000000 -941.2377748739724
62.9226515000000 -941.2378903605746
63.0043440000000 -941.2378981684135
63.7160150000000 -941.2378864590100
64.4085050000000 -941.2377753645115
65.1046835000000 -941.2375332100225
65.8049585000000 -941.2372030376584
66.5093925000000 -941.2367456992965
67.2180970000000 -941.2361992239395
67.9311515000000 -941.2355493856510
2.dat
является следующим:
54.6569312500000 -941.2300821583739
55.3555152500000 -941.2312112888004
56.1392347500000 -941.2326135552780
56.9291575000000 -941.2338291772218
57.6992532500000 -941.2348135408652
58.4711572500000 -941.2356230099117
59.2666985000000 -941.2362715934311
60.0547935000000 -941.2367074271724
60.8626545000000 -941.2370273047416
Обновление: используя @if.... подход:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
# Intial candidates for fit, per FU: - thus, the E vs V input data has to be per FU
E0_init = -941.510817926696
V0_init = 63.54960592453
B0_init = 76.3746233515232
B0_prime_init = 4.05340727164527
def BM(x, a, b, c, d):
return a + b*x + c*x**2 + d*x**3
def devBM(x, b, c, d):
return b + 2*c*x + 3*d*x**2
# Data 1 (Red triangles):
V_C_I, E_C_I = np.loadtxt('./1.dat', skiprows = 1).T
# Data 14 (Empty grey triangles):
V_14, E_14 = np.loadtxt('./2.dat', skiprows = 1).T
init_vals = [E0_init, V0_init, B0_init, B0_prime_init]
popt_C_I, pcov_C_I = curve_fit(BM, V_C_I, E_C_I, p0=init_vals)
popt_14, pcov_14 = curve_fit(BM, V_14, E_14, p0=init_vals)
from scipy.optimize import fsolve
def equations(p):
x1, x2 = p
E1 = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3]) - devBM(x2, popt_14[1], popt_14[2], popt_14[3])
E2 = ((BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - BM(x2, popt_14[0], popt_14[1], popt_14[2], popt_14[3])) / (x1 - x2)) - devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
return (E1, E2)
x1, x2 = fsolve(equations, (50, 60))
print 'x1 = ', x1
print 'x2 = ', x2
slope_common_tangent = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
print 'slope_common_tangent = ', slope_common_tangent
def comm_tangent(x, x1, slope_common_tangent):
return BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - slope_common_tangent * x1 + slope_common_tangent * x
# Linspace for plotting the fitting curves:
V_C_I_lin = np.linspace(V_C_I[0], V_C_I[-1], 10000)
V_14_lin = np.linspace(V_14[0], V_14[-1], 10000)
plt.figure()
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, BM(V_C_I_lin, *popt_C_I), color='black', label='Cubic fit Calcite I' )
p6, = plt.plot(V_14_lin, BM(V_14_lin, *popt_14), 'b', label='Cubic fit Calcite II')
xp = np.linspace(54, 68, 100)
pcomm_tangent, = plt.plot(xp, comm_tangent(xp, x1, slope_common_tangent), 'green', label='Common tangent')
# Plotting the scattered points:
p1 = plt.scatter(V_C_I, E_C_I, color='red', marker="^", label='Calcite I', s=100)
p5 = plt.scatter(V_14, E_14, color='grey', marker="^", facecolors='none', label='Calcite II', s=100)
fontP = FontProperties()
fontP.set_size('13')
plt.legend((p1, p2, p5, p6, pcomm_tangent), ("1", "Cubic fit 1", "2", 'Cubic fit 2', 'Common tangent'), prop=fontP)
print 'devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3]) = ', devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
plt.ylim(-941.240, -941.225)
plt.ticklabel_format(useOffset=False)
plt.show()
Я могу найти общую касательную, как показано ниже:
Однако эта общая касательная соответствует общей касательной в области за пределами диапазона данных, т.е. с использованием
V_C_I_lin = np.linspace(V_C_I[0]-30, V_C_I[-1], 10000)
V_14_lin = np.linspace(V_14[0]-20, V_14[-1]+2, 10000)
xp = np.linspace(40, 70, 100)
plt.ylim(-941.25, -941.18)
можно увидеть следующее:
Можно ли ограничить решатель диапазоном, в котором у нас есть данные, чтобы найти желаемую общую касательную?
Обновление 2.1: Используя @if.... подход с ограничением диапазона, следующий код дает x1 = 61.2569899
а также x2 = 59.7677843
, Если мы построим это:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import sys
from sympy import *
import sympy as sym
import os
# Intial candidates for fit, per FU: - thus, the E vs V input data has to be per FU
E0_init = -941.510817926696 # -1882.50963222/2.0
V0_init = 63.54960592453 #125.8532/2.0
B0_init = 76.3746233515232 #74.49
B0_prime_init = 4.05340727164527 #4.15
def BM(x, a, b, c, d):
return a + b*x + c*x**2 + d*x**3
def devBM(x, b, c, d):
return b + 2*c*x + 3*d*x**2
# Data 1 (Red triangles):
V_C_I, E_C_I = np.loadtxt('./1.dat', skiprows = 1).T
# Data 14 (Empty grey triangles):
V_14, E_14 = np.loadtxt('./2.dat', skiprows = 1).T
init_vals = [E0_init, V0_init, B0_init, B0_prime_init]
popt_C_I, pcov_C_I = curve_fit(BM, V_C_I, E_C_I, p0=init_vals)
popt_14, pcov_14 = curve_fit(BM, V_14, E_14, p0=init_vals)
def equations(p):
x1, x2 = p
E1 = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3]) - devBM(x2, popt_14[1], popt_14[2], popt_14[3])
E2 = ((BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - BM(x2, popt_14[0], popt_14[1], popt_14[2], popt_14[3])) / (x1 - x2)) - devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
return (E1, E2)
from scipy.optimize import least_squares
lb = (61.0, 59.0) # lower bounds on x1, x2
ub = (62.0, 60.0) # upper bounds
result = least_squares(equations, [61, 59], bounds=(lb, ub))
print 'result = ', result
# The result obtained is:
# x1 = 61.2569899
# x2 = 59.7677843
slope_common_tangent = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
print 'slope_common_tangent = ', slope_common_tangent
def comm_tangent(x, x1, slope_common_tangent):
return BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - slope_common_tangent * x1 + slope_common_tangent * x
# Linspace for plotting the fitting curves:
V_C_I_lin = np.linspace(V_C_I[0]-2, V_C_I[-1], 10000)
V_14_lin = np.linspace(V_14[0], V_14[-1]+2, 10000)
fig_handle = plt.figure()
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, BM(V_C_I_lin, *popt_C_I), color='black' )
p6, = plt.plot(V_14_lin, BM(V_14_lin, *popt_14), 'b' )
xp = np.linspace(54, 68, 100)
pcomm_tangent, = plt.plot(xp, comm_tangent(xp, x1, slope_common_tangent), 'green', label='Common tangent')
# Plotting the scattered points:
p1 = plt.scatter(V_C_I, E_C_I, color='red', marker="^", label='1', s=100)
p5 = plt.scatter(V_14, E_14, color='grey', marker="^", facecolors='none', label='2', s=100)
fontP = FontProperties()
fontP.set_size('13')
plt.legend((p1, p2, p5, p6, pcomm_tangent), ("1", "Cubic fit 1", "2", 'Cubic fit 2', 'Common tangent'), prop=fontP)
plt.ticklabel_format(useOffset=False)
plt.show()
Мы видим, что мы не получаем общую касательную:
2 ответа
Символическое нахождение корня
Ваша система уравнений состоит из квадратного уравнения и кубического уравнения. Не существует замкнутого символического решения такой системы. Действительно, если бы он был, можно было бы применить его к общему уравнению 5-й степени x**5 + a*x**4 + ... = 0
введя y = x**2
(квадратичный) и переписать исходное уравнение как x*y**2 + a*y**2 + ... = 0
(Кубический). И мы знаем, что этого нельзя сделать. Поэтому неудивительно, что SymPy не может решить эту проблему. Вам нужен числовой решатель (другая причина в том, что SymPy на самом деле не предназначен для решения уравнений, полных констант с плавающей точкой, они являются проблемой для символьных манипуляций).
Нахождение числового корня
SciPy fsolve - это первое, что приходит на ум. Вы могли бы сделать что-то вроде этого:
def F(x):
x1, x2 = x[0], x[1]
E1 = P(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3]) - P(x2, popt_14[1], popt_14[2], popt_14[3])
E2 = ((BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - BM(x2, popt_14[0], popt_14[1], popt_14[2], popt_14[3])) / (x1 - x2)) - P(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
return [E1, E2]
print fsolve(F, [50, 60]) # some reasonable initial point
Кстати, я бы переместился (x1-x2) из знаменателя в E2, переписав E2 как
(...) - (x1 - x2) * P(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
поэтому система является полиномиальной. Это, вероятно, сделает жизнь fsolve
немного проще
Ограничения диапазона: минимизация
ни fsolve
ни его родственники не любят root
поддержка размещения границ на переменных. Но вы можете использовать least_squares
который будет искать минимум суммы квадратов выражений E1, E2. Он поддерживает верхнюю и нижнюю границы, и, если повезет, минимальное значение ("стоимость") будет равно 0 в пределах точности машины, указывая на то, что вы нашли корень. Абстрактный пример (так как у меня нет ваших данных):
f1 = lambda x: 2*x**3 + 20
df1 = lambda x: 6*x**2 # derivative of f1.
f2 = lambda x: (x-3)**3 + x
df2 = lambda x: 3*(x-3)**2 + 1
def eqns(x):
x1, x2 = x[0], x[1]
eq1 = df1(x1) - df2(x2)
eq2 = df1(x1)*(x1 - x2) - (f1(x1) - f2(x2))
return [eq1, eq2]
from scipy.optimize import least_squares
lb = (2, -2) # lower bounds on x1, x2
ub = (5, 3) # upper bounds
least_squares(eqns, [3, 1], bounds=(lb, ub))
Выход:
active_mask: array([0, 0])
cost: 2.524354896707238e-29
fun: array([7.10542736e-15, 0.00000000e+00])
grad: array([1.93525199e-13, 1.34611132e-13])
jac: array([[27.23625045, 18.94483256],
[66.10672633, -0. ]])
message: '`gtol` termination condition is satisfied.'
nfev: 8
njev: 8
optimality: 2.4802477446153134e-13
status: 1
success: True
x: array([ 2.26968753, -0.15747203])
Стоимость очень мала, поэтому у нас есть решение, и это х. Как правило, присваивается вывод least_squares
к некоторой переменной res
и доступ res.x
оттуда.
Благодаря всей помощи @if...., запустив приведенный ниже код, размещенный в конце этого ответа, обсуждаются результаты 3 путей:
1) least_squares
с допусками по умолчанию:
#### ftol=1e-08, xtol=1e-08, gtol=1e-08 #####
result = active_mask: array([0, 0])
cost: 4.7190963603923405e-10
fun: array([ 1.60076424e-05, 2.62216448e-05])
grad: array([ -3.22762954e-09, -4.72137063e-09])
jac: array([[ 2.70753295e-04, -3.41257603e-04],
[ -2.88378229e-04, 2.82727898e-05]])
message: '`gtol` termination condition is satisfied.'
nfev: 4
njev: 4
optimality: 2.398161337354571e-09
status: 1
success: True
x: array([ 61.2569899, 59.7677843])
result.x = [ 61.2569899 59.7677843]
slope_common_tangent = -0.000533908881355
Стоимость равна нулю, и найденная общая касательная очень близка, но не идеальна:
2) least_squares
с более жесткими допусками:
#### ftol=1e-12, xtol=1e-12, gtol=1e-12 #####
result_tight_tols = active_mask: array([0, 0])
cost: 4.3861335617475759e-20
fun: array([ 2.08437035e-10, 2.10420231e-10])
grad: array([ -5.40980234e-16, -7.19344843e-14])
jac: array([[ 2.69431398e-04, -3.45167744e-04],
[ -2.69462978e-04, 5.34965061e-08]])
message: '`gtol` termination condition is satisfied.'
nfev: 8
njev: 8
optimality: 7.6628451334260651e-15
status: 1
success: True
x: array([ 61.35744106, 59.89347466])
result_tight_tols.x = [ 61.35744106 59.89347466]
slope_common_tangent = -0.000506777791299
Я ожидал, что стоимость будет выше, но по какой-то причине я не понимаю, стоимость еще ниже. Найденная общая касательная гораздо ближе:
3) Использование fsolve
но ограничивая регион
Мы видели в посте, что при использовании x1, x2 = fsolve(equations, (50, 60))
найденная общая касательная была неправильной (2-е и 3-е изображения). Тем не менее, если мы используем x1, x2 = fsolve(equations, (61.5, 62))
:
#### Using `fsolve`, but restricting the region: ####
x1 = 61.3574418449
x2 = 59.8934758773
slope_common_tangent = -0.000506777580856
Мы видим, что найденный склон очень похож на least_squares
с более жесткими допусками. Таким образом, найденная общая касательная также очень близка:
Эта таблица суммирует результаты:
Код, который производит эти три пути:
import numpy as np
from scipy.optimize import curve_fit
import matplotlib.pyplot as plt
from matplotlib.font_manager import FontProperties
import sys
from sympy import *
import sympy as sym
import os
import pickle as pl
# Intial candidates for fit, per FU: - thus, the E vs V input data has to be per FU
E0_init = -941.510817926696 # -1882.50963222/2.0
V0_init = 63.54960592453 #125.8532/2.0
B0_init = 76.3746233515232 #74.49
B0_prime_init = 4.05340727164527 #4.15
def BM(x, a, b, c, d):
return a + b*x + c*x**2 + d*x**3
def devBM(x, b, c, d):
return b + 2*c*x + 3*d*x**2
# Data 1 (Red triangles):
V_C_I, E_C_I = np.loadtxt('./1.dat', skiprows = 1).T
# Data 14 (Empty grey triangles):
V_14, E_14 = np.loadtxt('./2.dat', skiprows = 1).T
init_vals = [E0_init, V0_init, B0_init, B0_prime_init]
popt_C_I, pcov_C_I = curve_fit(BM, V_C_I, E_C_I, p0=init_vals)
popt_14, pcov_14 = curve_fit(BM, V_14, E_14, p0=init_vals)
def equations(p):
x1, x2 = p
E1 = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3]) - devBM(x2, popt_14[1], popt_14[2], popt_14[3])
E2 = ((BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - BM(x2, popt_14[0], popt_14[1], popt_14[2], popt_14[3])) / (x1 - x2)) - devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
return (E1, E2)
from scipy.optimize import least_squares
lb = (61.0, 59.0) # lower bounds on x1, x2
ub = (62.0, 60.0) # upper bounds
result = least_squares(equations, [61, 59], bounds=(lb, ub))
result_tight_tols = least_squares(equations, [61, 59], ftol=1e-12, xtol=1e-12, gtol=1e-12, bounds=(lb, ub))
print """
#### ftol=1e-08, xtol=1e-08, gtol=1e-08 #####
"""
print 'result = ', result
print 'result.x = ', result.x
print """
"""
x1 = result.x[0]
x2 = result.x[1]
slope_common_tangent = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
print 'slope_common_tangent = ', slope_common_tangent
def comm_tangent(x, x1, slope_common_tangent):
return BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - slope_common_tangent * x1 + slope_common_tangent * x
# Linspace for plotting the fitting curves:
V_C_I_lin = np.linspace(V_C_I[0]-2, V_C_I[-1], 10000)
V_14_lin = np.linspace(V_14[0], V_14[-1]+2, 10000)
plt.figure()
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, BM(V_C_I_lin, *popt_C_I), color='black' )
p6, = plt.plot(V_14_lin, BM(V_14_lin, *popt_14), 'b' )
xp = np.linspace(54, 68, 100)
pcomm_tangent, = plt.plot(xp, comm_tangent(xp, x1, slope_common_tangent), 'green', label='Common tangent')
# Plotting the scattered points:
p1 = plt.scatter(V_C_I, E_C_I, color='red', marker="^", label='1', s=100)
p5 = plt.scatter(V_14, E_14, color='grey', marker="^", facecolors='none', label='2', s=100)
fontP = FontProperties()
fontP.set_size('13')
plt.legend((p1, p2, p5, p6, pcomm_tangent), ("1", "Cubic fit 1", "2", 'Cubic fit 2', 'Common tangent'), prop=fontP)
plt.title('Least squares. Default tolerances: ftol=1e-08, xtol=1e-08, gtol=1e-08')
plt.ticklabel_format(useOffset=False)
### Tighter tolerances:
print """
#### ftol=1e-12, xtol=1e-12, gtol=1e-12 #####
"""
print 'result_tight_tols = ', result_tight_tols
print 'result_tight_tols.x = ', result_tight_tols.x
print """
"""
x1 = result_tight_tols.x[0]
x2 = result_tight_tols.x[1]
slope_common_tangent = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
print 'slope_common_tangent = ', slope_common_tangent
def comm_tangent(x, x1, slope_common_tangent):
return BM(x1, popt_C_I[0], popt_C_I[1], popt_C_I[2], popt_C_I[3]) - slope_common_tangent * x1 + slope_common_tangent * x
# Linspace for plotting the fitting curves:
V_C_I_lin = np.linspace(V_C_I[0]-2, V_C_I[-1], 10000)
V_14_lin = np.linspace(V_14[0], V_14[-1]+2, 10000)
plt.figure()
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, BM(V_C_I_lin, *popt_C_I), color='black' )
p6, = plt.plot(V_14_lin, BM(V_14_lin, *popt_14), 'b' )
xp = np.linspace(54, 68, 100)
pcomm_tangent, = plt.plot(xp, comm_tangent(xp, x1, slope_common_tangent), 'green', label='Common tangent')
# Plotting the scattered points:
p1 = plt.scatter(V_C_I, E_C_I, color='red', marker="^", label='1', s=100)
p5 = plt.scatter(V_14, E_14, color='grey', marker="^", facecolors='none', label='2', s=100)
fontP = FontProperties()
fontP.set_size('13')
plt.legend((p1, p2, p5, p6, pcomm_tangent), ("1", "Cubic fit 1", "2", 'Cubic fit 2', 'Common tangent'), prop=fontP)
plt.title('ftol=1e-08, xtol=1e-08, gtol=1e-08')
plt.ticklabel_format(useOffset=False)
plt.title('Lest Squares. Tightening tolerances: ftol=1e-12, xtol=1e-12, gtol=1e-12')
print """
#### Using `fsolve`, but restricting the region: ####
"""
from scipy.optimize import fsolve
x1, x2 = fsolve(equations, (61.5, 62))
print 'x1 = ', x1
print 'x2 = ', x2
slope_common_tangent = devBM(x1, popt_C_I[1], popt_C_I[2], popt_C_I[3])
print 'slope_common_tangent = ', slope_common_tangent
plt.figure()
# Plotting the fitting curves:
p2, = plt.plot(V_C_I_lin, BM(V_C_I_lin, *popt_C_I), color='black' )
p6, = plt.plot(V_14_lin, BM(V_14_lin, *popt_14), 'b' )
xp = np.linspace(54, 68, 100)
pcomm_tangent, = plt.plot(xp, comm_tangent(xp, x1, slope_common_tangent), 'green', label='Common tangent')
# Plotting the scattered points:
p1 = plt.scatter(V_C_I, E_C_I, color='red', marker="^", label='1', s=100)
p5 = plt.scatter(V_14, E_14, color='grey', marker="^", facecolors='none', label='2', s=100)
fontP = FontProperties()
fontP.set_size('13')
plt.legend((p1, p2, p5, p6, pcomm_tangent), ("1", "Cubic fit 1", "2", 'Cubic fit 2', 'Common tangent'), prop=fontP)
plt.ticklabel_format(useOffset=False)
plt.title('Using `fsolve`, but restricting the region')
plt.show()