I have equation:
import numpy as np
from scipy import optimize
def wealth_evolution(price, wealth=10, rate=0.01, q=1, realEstate=0.1, prev_price=56):
sum_wantedEstate = 100
for delta in range(1,4):
z = rate - ((price-prev_price) / (price + q / rate))
k = delta * np.divide(1.0, float(np.maximum(0.0, z)))
wantedEstate = (wealth / (price + q / rate)) * np.minimum(k, 1) - realEstate
sum_wantedEstate += wantedEstate
return sum_wantedEstate
So I find the solution of this equation:
sol = optimize.fsolve(wealth_evolution, 200)
But if I substituted sol into equation I wouldn't get 0 (welth_evolution(sol)). Why it happens? fsolve finds the roots of f(x)=0.
UPD:
The full_output gives:
(array([ 2585200.]), {'qtf': array([-99.70002298]), 'nfev': 14, 'fjac': array([[-1.]]), 'r': array([ 3.45456519e-11]), 'fvec': array([ 99.7000116])}, 5, 'The iteration is not making good progress, as measured by the \n improvement from the last ten iterations.')
Have you tried plotting your function?
import numpy as np
from scipy import optimize
from matplotlib import pyplot as plt
small = 1e-30
def wealth_evolution(price, wealth=10, rate=0.01, q=1, realEstate=0.1, prev_price=56):
sum_wantedEstate = 100
for delta in range(1,4):
z = rate - ((price-prev_price) / (price + q / rate))
k = delta * np.divide(1.0, float(np.maximum(small, z)))
wantedEstate = (wealth / (price + q / rate)) * np.minimum(k, 1) - realEstate
sum_wantedEstate += wantedEstate
return sum_wantedEstate
price_range = np.linspace(0,10000,10000)
we = [wealth_evolution(p) for p in price_range]
plt.plot(price_range,we)
plt.xlabel('price')
plt.ylabel('wealth_evolution(price)')
plt.show()
At least for the parameters you specify it does not have a root, which is what fsolve tries to find. If you want to minimize a function you can try fmin. For this function this will not help though, because it seems to just asymptotically decay to 99.7 or so. So minimizing it would lead to infinite price.
So either you have to live with this or come up with a different function to optimize or constrain your search range (in which case you don't have to search, because it will just be the maximum value...).
Related
I have the following code where I need to solve an expression to find the roots. The expression needs to be solved for omega.
import numpy as np
from sympy import Symbol,lambdify
import scipy
from mpmath import findroot, exp
eta = 1.5
tau = 5 /1000
omega = Symbol("omega")
Tf = exp(1j * omega * tau)
symFun = 1 + Tf * (eta - 1)
denom = lambdify((omega), symFun, "scipy")
Tf_high = 1j * 2 * np.pi * 1000 * tau
sol = findroot(denom, [0+1j,Tf_high])
The program gives an error and I am not able to correct. The error is : TypeError: cannot create mpf from 0.005Iomega
Edit 1 - I have tried to implement different approach based on comments. First approach was to use the sympy.solveset module. Second approach was to use fsolve from scipy.optimise. Both are not giving proper output.
For clarity, I am copying the relevant code to each approach along with the output I am getting.
Approach 1 - Sympy
import numpy as np
from sympy import Symbol,exp
from sympy.solvers.solveset import solveset,solveset_real,solveset_complex
import matplotlib.pyplot as plt
def denominator(eta,Tf):
return 1 + Tf * (eta - 1)
if __name__ == "__main__":
eta = 1.5
tau = 5 /1000
omega = Symbol("omega")
n = 1
Tf = exp(1j * omega * tau)
denom = 1 + Tf * (eta - 1)
symFun = denominator(eta,Tf)
sol = solveset_real(denom,omega)
sol1 = solveset_complex(denom,omega)
print('In real domain', sol)
print('In imaginary domain',sol1)
Output:
In real domain EmptySet
In imaginary domain ImageSet(Lambda(_n, -200.0*I*(I*(2*_n*pi + pi) + 0.693147180559945)), Integers)
Approach 2 Scipy
import numpy as np
from scipy.optimize import fsolve, root
def denominator(eta,tau,n, omega):
Tf = n * np.exo(1j * omega * tau)
return 1 + Tf * (eta - 1)
if __name__ == "__main__":
eta = 1.5
tau = 5 /1000
n = 1
func = lambda omega : 1 + (eta - 1) * (n * np.exp( 1j * omega * tau))
sol = fsolve(func,10)
print(sol)
Output:
Cannot cast array data from dtype('complex128') to dtype('float64') according to the rule 'safe'
How do I correct the program? Please suggest me the approach that will give proper results.
SymPy is a computer algebra system and solves the equation like a human would. SciPy uses numeric optimization. If you want ALL the solutions, I suggest going with SymPy. If you want one solution, I suggest going with SciPy.
Approach 1 - SymPy
The solutions SymPy gives will be more "interactive" for you as the developer. But it will be perfectly correct almost all the time.
from sympy import *
eta = S(3)/2
tau = S(5) / 1000
omega = Symbol("omega")
n = 1
Tf = exp(I * omega * tau)
denom = 1 + Tf * (eta - 1)
sol = solveset(denom, omega)
print(sol)
Giving
ImageSet(Lambda(_n, -200*I*(I*(2*_n*pi + pi) + log(2))), Integers)
This is the true mathematical solution.
Notice how I put S around an integer before dividing it. When dividing integers in Python, it loses accuracy because it uses floating point numbers. Converting it to SymPy objects keep all the accuracy.
Since we know we have an ImageSet over integers, we can start listing a few solutions:
for n in range(-3, 3):
print(complex(sol.lamda(n)))
Which gives
(-3141.5926535897934-138.62943611198907j)
(-1884.9555921538758-138.62943611198907j)
(-628.3185307179587-138.62943611198907j)
(628.3185307179587-138.62943611198907j)
(1884.9555921538758-138.62943611198907j)
(3141.5926535897934-138.62943611198907j)
With some experience, you could automate it so that the whole program only returns 1 solution no matter on the type of output returned by solveset.
Approach 2 - SciPy
The solutions SciPy gives will be more automated. You will never have a perfect answer and different choices of the initial conditions may not converge all the time.
import numpy as np
from scipy.optimize import root
eta = 1.5
tau = 5 / 1000
n = 1
def f(omega: Tuple):
omega_real, omega_imag = omega
omega: complex = omega_real + omega_imag*1j
result: complex = 1 + (eta - 1) * (n * np.exp(1j * omega * tau))
return result.real, result.imag
sol = root(f, [100, 100])
print(sol)
print(sol.x[0]+sol.x[1]*1j)
Which gives
fjac: array([[ 0.00932264, 0.99995654],
[-0.99995654, 0.00932264]])
fun: array([-2.13074003e-12, -8.86389816e-12])
message: 'The solution converged.'
nfev: 30
qtf: array([ 2.96274855e-09, -6.82780898e-10])
r: array([-0.00520194, -0.00085702, -0.00479143])
status: 1
success: True
x: array([ 628.31853072, -138.62943611])
(628.3185307197314-138.62943611241522j)
Looks like that's one of the solutions SymPy found. So we must be doing something right. Note that there are many initial values that don't converge, for example, sol = root(f, [1, 1]).
I asked a similar question in January that #Miłosz Wieczór was kind enough to answer. Now, I am faced with a similar but different challenge since I need to fit two parameters (fc and alpha) simultaneously on two datasets (e_exp and iq_exp). I basically need to find the values of fc and alpha that are the best fits to both data e_exp and iq_exp.
import numpy as np
import math
from scipy.optimize import curve_fit, least_squares, minimize
f_exp = np.array([1, 1.6, 2.7, 4.4, 7.3, 12, 20, 32, 56, 88, 144, 250000])
e_exp = np.array([7.15, 7.30, 7.20, 7.25, 7.26, 7.28, 7.32, 7.25, 7.35, 7.34, 7.37, 11.55])
iq_exp = np.array([0.010, 0.009, 0.011, 0.011, 0.010, 0.012, 0.019, 0.027, 0.038, 0.044, 0.052, 0.005])
ezero = np.min(e_exp)
einf = np.max(e_exp)
ig_fc = 500
ig_alpha = 0.35
def CCRI(f_exp, fc, alpha):
x = np.log(f_exp/fc)
R = ezero + 1/2 * (einf - ezero) * (1 + np.sinh((1 - alpha) * x) / (np.cosh((1 - alpha) * x) + np.sin(1/2 * alpha * math.pi)))
I = 1/2 * (einf - ezero) * np.cos(alpha * math.pi / 2) / (np.cosh((1 - alpha) * x) + np.sin(alpha * math.pi / 2))
RI = np.sqrt(R ** 2 + I ** 2)
return RI
def CCiQ(f_exp, fc, alpha):
x = np.log(f_exp/fc)
R = ezero + 1/2 * (einf - ezero) * (1 + np.sinh((1 - alpha) * x) / (np.cosh((1 - alpha) * x) + np.sin(1/2 * alpha * math.pi)))
I = 1/2 * (einf - ezero) * np.cos(alpha * math.pi / 2) / (np.cosh((1 - alpha) * x) + np.sin(alpha * math.pi / 2))
iQ = I / R
return iQ
poptRI, pcovRI = curve_fit(CCRI, f_exp, e_exp, p0=(ig_fc, ig_alpha))
poptiQ, pcoviQ = curve_fit(CCiQ, f_exp, iq_exp, p0=(ig_fc, ig_alpha))
einf, ezero, and f_exp are all constant plus the variables I need to optimize are ig_fc and ig_alpha, in which ig stands for initial guess. In the code above, I get two different fc and alpha values because I solve them independently. I need however to solve them simultaneously so that fc and alpha are universal.
Is there a way to solve two different functions to provide universal solutions for fc and alpha?
The docs state on the second returned value from curve_fit:
pcov
The estimated covariance of popt. The diagonals provide the variance of the parameter estimate. To compute one standard deviation
errors on the parameters use perr = np.sqrt(np.diag(pcov)).
So if you want to minimize the overall error, you need to combine the errors of both your fits.
def objective(what, ever):
poptRI, pcovRI = curve_fit(CCRI, f_exp, e_exp, p0=(ig_fc, ig_alpha))
poptiQ, pcoviQ = curve_fit(CCiQ, f_exp, iq_exp, p0=(ig_fc, ig_alpha))
# not sure if this the correct equation, but you can start with it
err_total = np.sum(np.sqrt(np.diag(pcovRI))) + np.sum(np.sqrt(np.diag(pcoviQ)))
return err_total
On total errors of 2d Gaussian functions:
https://www.visiondummy.com/2014/04/draw-error-ellipse-representing-covariance-matrix/
Update:
Since you want poptRI and poptiQ to be the same, you need to minimize their distance.
This can be done like
from numpy import linalg
def objective(what, ever):
poptRI, pcovRI = curve_fit(CCRI, f_exp, e_exp, p0=(ig_fc, ig_alpha))
poptiQ, pcoviQ = curve_fit(CCiQ, f_exp, iq_exp, p0=(ig_fc, ig_alpha))
delta = linalg.norm(poptiQ - poptRI)
return delta
Minimizing this function will (should) result in similar values for poptRI and poptiQ. You take the parameters as vectors, and try to minimize the length of their delta vector.
However, this approach assumes that poptRI and poptiQ (and their coefficients) are about in the same range since you are using some metric on them. If say one if them is in the range 2000 and the other in the range 2. Then the optimizer will favour tuning the first one. But maybe this is fine.
If you somehow want to treat them the same you need to normalize them.
One approach (assuming all coefficients are similar) could be
linalg.norm((poptiQ / linalg.norm(poptiQ)) - (poptRI / linalg.norm(poptRI))))
You normalize the results to unit vectors, then subtract them, then create the norm.
The same is true for the inputs to the function, but it might not be that important there. See the links below.
But this strongly depends on the problem you are trying to solve. There is no general solution.
Some links related to this:
Is normalization useful/necessary in optimization?
Why do we have to normalize the input for an artificial neural network?
Another objective function:
It this what you are trying to do?
You want to find the best fc and alpha so the fit results of both functions are as close as possible?
def objective(fc, alpha):
poptRI, pcovRI = curve_fit(CCRI, f_exp, e_exp, p0=(fc, alpha))
poptiQ, pcoviQ = curve_fit(CCiQ, f_exp, iq_exp, p0=(fc, alpha))
delta = linalg.norm(poptiQ - poptRI)
return delta
I am trying to maximize a target function f(x) with function scipy.optimize.minimum. But it usually takes 4-5 hrs to run the code because the function f(x) involves a lot of computation of complex matrix. To improve its speed, I want to use gpu. And I've already tried tensorflow package. Since I use numpy to define f(x), I have to convert it into tensorflow's format. However, it doesn't support the computation of complex matrix. What else package or means I can use? Any suggestions?
To specific my problem, I will show calculate scheme below:
Calculate the expectation :
-where H=x*H_0, x is the parameter
Let \phi go through the dynamics of Schrödinger equation
-Different H is correspond to a different \phi_end. Thus, parameter x determines the expectation
Change x, calculate the corresponding expectation
Find a specific x that minimize the expectation
Here is a simple example of part of my code:
import numpy as np
import cmath
from scipy.linalg import expm
import scipy.optimize as opt
# create initial complex matrixes
N = 2 # Dimension of matrix
H = np.array([[1.0 + 1.0j] * N] * N) # a complex matrix with shape(N, N)
A = np.array([[0.0j] * N] * N)
A[0][0] = 1.0 + 1j
# calculate the expectation
def value(phi):
exp_H = expm(H) # put the matrix in the exp function
new_phi = np.linalg.linalg.matmul(exp_H, phi)
# calculate the expectation of the matrix
x = np.linalg.linalg.matmul(H, new_phi)
expectation = np.inner(np.conj(phi), x)
return expectation
# Contants
tmax = 1
dt = 0.1
nstep = int(tmax/dt)
phi_init = [1.0 + 1.0j] * N
# 1st derivative of Schrödinger equation
def dXdt(t, phi, H): # 1st derivative of the function
return -1j * np.linalg.linalg.matmul(H, phi)
def f(X):
phi = [[0j] * N] * nstep # store every time's phi
phi[0] = phi_init
# phi go through the dynamics of Schrödinger equation
for i in range(nstep - 1):
phi[i + 1] = phi[i] - dXdt(i * dt, X[i] * H, phi[i]) * dt
# calculate the corresponding value
f_result = value(phi[-1])
return f_result
# Initialize the parameter
X0 = np.array(np.ones(nstep))
results = opt.minimize(f, X0) # minimize the target function
opt_x = results.x
PS:
Python Version: 3.7
Operation System: Win 10
I am trying to fit two sweeps that take on a Fraunhofer distribution. Please help me to understand a snippet in my code to make it run better, because it changes the fitting, but I don't know why! This is the line I mean specifically:
if MaxIndexup>MaxIndexdown: FitDataup = TempData.loc[TempData["Field (Oe)"] <= MaxIndexup]
When this runs, it puts a bunch of files with a lot of data that is irrelevant to the question at hand. Which is how do you identify the peak of the (almost) Gaussian distribution, and then how do you fit it so the lobes work better (if you understand lobes given by a Fraunhofer diffraction pattern). I have my code working, I just want/need to understand more so how to parse through and fit the data better:
from os import walk
import numpy as np
import pandas as pd
import re
import math
from scipy import stats
import scipy.special as sp
from lmfit import Model
#create function that computes the Fruanhofer diffraction pattern: reference--> https://en.wikipedia.org/wiki/Fraunhofer_diffraction; also see the Guassian section
def Fraunup(x, Imaxup, Lambda, M, CorrectedBitSize):
y1 = Imaxup * abs(2*sp.jv(1,((float(CorrectedBitSize)*(2*abs(Lambda) + 2*dN + dF)*(x+M))/(phi0*math.pow(10,10))))/((float(CorrectedBitSize)*(2*Lambda + 2*dN + dF)*(x+M))/(phi0*math.pow(10,10))))
return y1
def Fraundown(x, Imaxdown, Lambda, M, CorrectedBitSize):
y2 = Imaxdown * abs(2 * sp.jv(1, ((float(CorrectedBitSize) * (2 * abs(Lambda) + 2 * dN + dF) * (x + M)) / (phi0 * math.pow(10, 10)))) / ( (float(CorrectedBitSize) * (2 * Lambda + 2 * dN + dF) * (x + M)) / (phi0 * math.pow(10, 10))))
return y2
Iparamup = 1.5 * TempData["Ic+ (mA)"].max()
Iparamdown = 1.5 * TempData2["Ic+ (mA)"].max()
Lambparam = (0.00000009)
Mparam = (200 / CorrectedBitSize_Squared) #gives you H_shift
#Create the Model and add specfic parameters. M deals with the H_Shift
modelup = Model(Fraunup)
params = modelup.make_params()
params.add('Imaxup',value=Iparamup)
params.add('Lambda',value=Lambparam)
params.add('M',value=np.mean(Mparam),min=0,max=500)
modeldown = Model(Fraundown)
params = modeldown.make_params()
params.add('Lambda',value=Lambparam)
params.add('M',value=np.mean(Mparam),min=0,max=500)
params.add('Imaxdown', value=Iparamdown)
if MaxIndexup>MaxIndexdown:
FitDataup = TempData.loc[TempData["Field (Oe)"] <= MaxIndexup]
FitDataup = FitDataup[~np.isnan(FitDataup['Ic+ (mA)'])].reset_index()
FitDatadown = FitDatadown[~np.isnan(FitDatadown['Ic+ (mA)'])].reset_index()
for x in range(len(Mparam)):
resultsup = modelup.fit(FitDataup['Ic+ (mA)'],x=-FitDataup['Field (Oe)'], CorrectedBitSize=CorrectedBitSize[x], Imaxup=Iparamup, Lambda=Lambparam, M= Mparam[x], method='Powell')
resultsdown = modeldown.fit(FitDatadown['Ic+ (mA)'], x=-FitDatadown['Field (Oe)'], CorrectedBitSize=CorrectedBitSize[x],Imaxdown=Iparamdown, Lambda=Lambparam, M=Mparam[x], method='Powell')
#fit the value using the best results
YFitValueup = Fraunup(TempData["Field (Oe)"], resultsup.best_values['Imaxup'], resultsup.best_values['Lambda'], resultsup.best_values['M'], resultsup.best_values['CorrectedBitSize'])
YFitValuedown = Fraundown(TempData2["Field (Oe)"], resultsdown.best_values['Imaxdown'], resultsdown.best_values['Lambda'], resultsdown.best_values['M'], resultsdown.best_values['CorrectedBitSize'])
I'm just trying to plot two gaussians and to find the intersection point. I have the following code. It's not plotting the exact intersection though and I really cannot figure out why. It's like just barely slightly off but I worked through the derived solution if we took the log of subtracted gaussians and yeah it seems like it should be correct. Can anyone help? Thank you so much!
import numpy as np
import matplotlib.pyplot as plt
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
# found online
def solve_gasussians(m1, s1, m2, s2):
a = 1.0/(2.0*s1**2) - 1.0/(2.0*s2**2)
b = m2/(s2**2) - m1/(s1**2)
c = m1**2 /(2*s1**2) - m2**2 / (2.0*s2**2) - np.log(s2/s1)
return np.roots([a,b,c])
s1 = np.linspace(0, 10,300)
s2 = np.linspace(0, 14, 300)
solved_val = solve_gasussians(5.0, 0.5, 7.0, 1.0)
print solved_val
solved_val = solved_val[0]
plt.figure('Baseline Distributions')
plt.title('Baseline Distributions')
plt.xlabel('Response Rate')
plt.ylabel('Probability')
plt.plot(s1, plot_normal(s1, 5.0, 0.5),'r', label='s1')
plt.plot(s2, plot_normal(s2, 7.0, 1.0),'b', label='s2')
plt.plot(solved_val, plot_normal(solved_val, 7.0, 1.0), 'mo')
plt.legend()
plt.show()
You have a small bug in plot_normal function - you are missing square root in the denominator. Proper version:
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/np.sqrt(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
gives the expected result:
And two remarks.
Remember that you can have 2 roots of the equation in general (two intersection points), and this is the case with parameters you provided.
As far as I know np.roots gives you approximate result, but you cat get exact result easily, rewriting solve_gasussians function as:
def solve_gasussians(m1, s1, m2, s2):
# coefficients of quadratic equation ax^2 + bx + c = 0
a = (s1**2.0) - (s2**2.0)
b = 2 * (m1 * s2**2.0 - m2 * s1**2.0)
c = m2**2.0 * s1**2.0 - m1**2.0 * s2**2.0 - 2 * s1**2.0 * s2**2.0 * np.log(s1/s2)
x1 = (-b + np.sqrt(b**2.0 - 4.0 * a * c)) / (2.0 * a)
x2 = (-b - np.sqrt(b**2.0 - 4.0 * a * c)) / (2.0 * a)
return x1, x2
I don't know where the mistake lies in your code. But I think I found the code your borrowed from and made part of the adjustment you need.
import numpy as np
import matplotlib.pyplot as plt
from scipy.stats import norm
def solve(m1,m2,std1,std2):
a = 1/(2*std1**2) - 1/(2*std2**2)
b = m2/(std2**2) - m1/(std1**2)
c = m1**2 /(2*std1**2) - m2**2 / (2*std2**2) - np.log(std2/std1)
return np.roots([a,b,c])
m1 = 5
std1 = 0.5
m2 = 7
std2 = 1
result = solve(m1,m2,std1,std2)
x = np.linspace(-5,9,10000)
plot1=plt.plot(x,[norm.pdf(_,m1,std1) for _ in x])
plot2=plt.plot(x,[norm.pdf(_,m2,std2) for _ in x])
plot3=plt.plot(result[0],norm.pdf(result[0],m1,std1) ,'o')
plt.show()
I will offer two pieces of unsolicited advice that might make life easier for you (in the way they do for me):
When you adapt code try to make small, incremental changes and check that the code still works at each step.
Look for existing free libraries. In this case norm from scipy is a good replacement for what was used in the original code.
The mistake is here. This line:
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
Should be this:
def plot_normal(x, mean = 0, sigma = 1):
return 1.0/np.sqrt(2*np.pi*sigma**2) * np.exp(-((x-mean)**2)/(2*sigma**2))
You forgot the sqrt.
It would be wiser to use a pre-existing normal pdf if that's available, such as:
import scipy.stats
def plot_normal(x, mean = 0, sigma = 1):
return scipy.stats.norm.pdf(x,loc=mean,scale=sigma)
It's also possible to solve for the intersections exactly. This answer provides a quadratic equation for the roots of the Gaussians' intersections. Using maxima to solve for x gives the following expression. Which, while complicated, does not rely on iterative methods and can be automatically generated from simpler expressions.
def solve_gaussians(m1,s1,m2,s2):
x1 = (s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)+m1*s2**2-m2*s1**2)/(s2**2-s1**2)
x2 = -(s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)-m1*s2**2+m2*s1**2)/(s2**2-s1**2)
return x1,x2
Putting it altogether gives:
import numpy as np
import matplotlib.pyplot as plt
import scipy.stats
def plot_normal(x, mean = 0, sigma = 1):
return scipy.stats.norm.pdf(x,loc=mean,scale=sigma)
#Use the equation from [this answer](https://stats.stackexchange.com/a/12213/12116) solved for x
def solve_gaussians(m1,s1,m2,s2):
x1 = (s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)+m1*s2**2-m2*s1**2)/(s2**2-s1**2)
x2 = -(s1*s2*np.sqrt((-2*np.log(s1/s2)*s2**2)+2*s1**2*np.log(s1/s2)+m2**2-2*m1*m2+m1**2)-m1*s2**2+m2*s1**2)/(s2**2-s1**2)
return x1,x2
s = np.linspace(0, 14,300)
x = solve_gaussians(5.0,0.5,7.0,1.0)
plt.figure('Baseline Distributions')
plt.title('Baseline Distributions')
plt.xlabel('Response Rate')
plt.ylabel('Probability')
plt.plot(s, plot_normal(s, 5.0, 0.5),'r', label='s1')
plt.plot(s, plot_normal(s, 7.0, 1.0),'b', label='s2')
plt.plot(x[0],plot_normal(x[0],5.,0.5),'mo')
plt.plot(x[1],plot_normal(x[1],5.,0.5),'mo')
plt.legend()
plt.show()
Giving: