I am given the following bond:
and need to fit the Vasicek model to this data.
My attempt is the following:
# ... imports
years = np.array([1, 2, 3, 4, 7, 10])
pric = np.array([0, .93, .85, .78, .65, .55, .42])
X = sympy.symbols("a b sigma")
a, b, s = X
rt1_rt = np.diff(pric)
ab_rt = np.array([a*(b-r) for r in pric[1:] ])
term = rt1_rt - ab_rt
def normpdf(x, mean, sd):
var = sd**2
denom = (2*sym.pi*var)**.5
num = sym.E**(-(x-mean)**2/(2*var))
return num/denom
pdfs = np.array([sym.log(normpdf(x, 0, s)) for x in term])
func = 0
for el in pdfs:
func += el
func = func.factor()
lmd = sym.lambdify(X, func)
def target_fun(params):
return lmd(*params)
result = scipy.optimize.least_squares(target_fun, [10, 10, 10])
I don't think that it outputs correct solution.
Your code is almost correct.
You want to maximize your function, therefore you need to place minus sign in front of lmd in your function.
def target_fun(params):
return -lmd(*params)
Additionally, the initial values are usually set to less than 1. Picking 10 is not the best choice as the algorithm might converge to a saddle point.
Consider [0.01, 0.01, 0.01].
Related
I have a 3D array and I want to find the optimal parameters corresponding to a local maximum of 2D array for each iteration of 3rd array as an outer loop there.
Nstep1 = 5
l2= linspace(0.01,2,Nstep1)
EP_opt = zeros(Nstep1)
Nstep = 5
for l in range(Nstep1):
Vp = zeros((Nstep, Nstep))
g1 = linspace(0.1, 0.5, Nstep)
g2 = linspace(0.1, 0.5, Nstep)
for j in range(Nstep):
for k in range(Nstep):
def Ep(pr):
a,b,c=pr
return -(a*l2[l]+b*g1[j]*g1[j]-c*g2[k])
x0=[0,1,1]
bnds= ((0, 1),(0, 1),(0, 1))
res=minimize(Ep,x0,bounds=bnds)
#Vp[j,k,l] = -res.fun# will it help to find local maximum of `Vp` for each `l`.
Vp[j,k] = -res.fun
x1= res.x
EP_opt[l] = Vp.max()# local maximum for each `l2`
how to find the optimized parameters (x1[0], x1[1], x1[2],g1[j] and g2[k]) corresponding to Vp.max() for each l2? Thanks.
Your optimisation doesn't make any sense, because - given your objective function - for every index of l, the best parameters remain the same. Run this:
import numpy as np
from scipy.optimize import minimize
def Ep(pr: np.ndarray, l: float) -> float:
a, b, c, g1, g2 = pr
return -(a*l + b*g1*g1 - c*g2)
def main() -> None:
Nstep1 = 5
Nstep = 5
l2 = np.linspace(0.01, 2, Nstep1) # 5-vector of coefficients
g1 = np.linspace(0.1, 0.5, Nstep) # 5-vector of coefficients
g2 = g1 # 5-vector of coefficients
# for every l,j,k and parameters x1,2,3 there is a value of Ep
# the optimisation parameters exclude l and include j,k,x1,2,3
# over those optimisation parameters, there is some maximum Ep.
initial = np.zeros(5)
for l, l_val in enumerate(l2):
res = minimize(
Ep, x0=initial,
bounds=(
(0, 1), (0, 1), (0, 1),
(g1[0], g1[-1]),
(g2[0], g2[-1]),
),
args=(l_val,),
)
print(res)
if __name__ == '__main__':
main()
It will show you that in all five cases of l, the best parameter set for x0,1,2,g1,g2 is
[1. , 1. , 0. , 0.5, 0.1]
I'm running a multi-objective optimisation with Pymoo (0.5.0) using NSGA-III and within my population of new candidates some of the generated candidates have nan parameters. This results in my evaluate function (which is a call to a neural network) returning nan. The optimisation is running and producing desired results but I'd like to know why some of the candidate parameters are nan. Here is the code for the problem.
Problem setup:
opt_name = "sopt00001KaRxD60fLn2"
pop_size = 165
n_gen = 350
cross_over_pb = 0.9
mutation_pb = 0.1
# Fixed params
band ="KaRx"
arc = "RevF"
source_spec = "sweep99p1"
lens_diameter = 60.0
source_z = 5.0
r_lam = 0.1
use_original_transformation = 0 # false
source_x0 = 0.0
target_scans = [0, 70, 50]
# Optimisation param ranges
lens_material_delta_n = [1.5, 3.6]
lens_thick = [5, 35]
lens_radii_back = [39, 22500]
lens_radii_front = [39, 22500]
source_width = [2, 20]
source_x = [12, 20]
params_lower_lim = [lens_thick[0], lens_radii_front[0], lens_radii_back[0], source_width[0], source_x[0], source_x[0],
lens_material_delta_n[0], -1, -1, -1, 0, -1, -1, -1, 0]
params_upper_lim = [lens_thick[1], lens_radii_front[1], lens_radii_back[1], source_width[1], source_x[1], source_x[1],
lens_material_delta_n[1], 1, 1, 1, 1, -1, -1, -1, 1]
n_var = len(params_lower_lim)
assert n_var == len(params_upper_lim), print("Upper and lower parameter limits are not equal length!")
# Other required params
if band == "KaRx":
freq_center = 19.45
freq_width = 3.5
Evaluate function:
class ProblemWrapper(Problem):
def _evaluate(self, params, out, *args, **kwargs):
res = []
for param in params:
source_x70 = source_x_f(param[4], param[5], source_x, 50, r_lam, target_scans, freq_center, freq_width)
source_x50 = source_x_f(param[4], param[5], source_x, 70, r_lam, target_scans, freq_center, freq_width)
res.append(smeep(band,
lens_diameter, param[0],
param[1], param[2],
param[3],
source_x0, source_x70, source_x50,
source_z,
param[6], param[7], param[8], param[9], param[10], param[11], param[12], param[13], param[14],
r_lam, use_original_transformation,
arc,
source_spec,
target_scans))
out['F'] = np.array(res)
Algorithm settings:
ref_dirs = get_reference_directions("das-dennis", 3, n_partitions=12)
problem = ProblemWrapper(n_var=n_var,
n_obj=len(target_scans),
xl=params_lower_lim,
xu=params_upper_lim)
algorithm = NSGA3(
pop_size=pop_size,
ref_dirs=ref_dirs,
sampling=get_sampling("real_random"),
cross_over=get_crossover("real_sbx", prob=cross_over_pb),
mutation=get_mutation("real_pm", prob=mutation_pb)
)
Execution:
res = minimize(problem=problem,
algorithm=algorithm,
termination=("n_gen", n_gen),
save_history=True,
verbose=True
)
It looks like the only affected parameters are the poly6 (param[11]), poly7 (param[12]) and poly8 (param[13]) terms. And it differs candidate to candidate. I confess I have not tried any different crossover or mutation schemes but these seemed the best from the documentation.
Thanks in advance!
The nan arise because the limits for your parameters 11, 12 and 12 are equal (-1 and -1 in all cases).
If you look at the code for the polynomial mutation (real_pm), you have the following lines:
delta1 = (X - xl) / (xu - xl)
delta2 = (xu - X) / (xu - xl)
where xu and xl are the upper and lower bounds of the parameters. In your case, that would cause a divide-by-0.
Since the limits are the same (if this is correct), they are actually not part of the optimization and you should remove them from the list.
I'm trying to use the multinominal.pmf function from scipy.stats (python).
When I use this function where all probabilities in the input bigger than zero, it work fine. The problem is when I want to use the function when one of the probabilities is zero.
The following example shows what I mean:
In [18]: multinomial.pmf([3, 3, 0], 6, [1/3.0, 1/3.0, 1/3.0])
Out[18]: 0.027434842249657095
In [19]: multinomial.pmf([3, 3, 0], 6, [2/3.0, 1/3.0, 0])
Out[19]: nan
As can be seen, in the first time where all probabilities > 0 there is no problem to use the function. However when I change one of the probabilities to zero, the function return nan, even through the function should return 0.21948.
Is there a way (in python) to calculate the pmf when one of the probabilities is zero? either another way that can handle it, or a work around for this function.
additional info
The value that the function in the example should have returned I calculated using mnpdf function in matlab. However since the rest of my code is in python I prefer to find a way to calculate it in python.
Good spot! This is a bug in scipy. The source code can be found here.
Line 3031 to 3051:
def pmf(self, x, n, p):
return np.exp(self.logpmf(x, n, p))
Line 2997 to 3017:
def logpmf(self, x, n, p):
n, p, npcond = self._process_parameters(n, p)
Line 2939 to 2958:
def _process_parameters(self, n, p):
p = np.array(p, dtype=np.float64, copy=True)
p[...,-1] = 1. - p[...,:-1].sum(axis=-1)
# true for bad p
pcond = np.any(p <= 0, axis=-1) # <- Here is why!!!
pcond |= np.any(p > 1, axis=-1)
n = np.array(n, dtype=np.int, copy=True)
# true for bad n
ncond = n <= 0
return n, p, ncond | pcond
The line pcond = np.any(p <= 0, axis=-1) results in pcond being true if any value of p is <= 0.
Then in logpmf line 3029:
return self._checkresult(result, npcond_, np.NAN)
results in logpmf and pmf returning nan!
Note that the actual result is calculated properly (line 3020, 2994-2995):
result = self._logpmf(x, n, p)
def _logpmf(self, x, n, p):
return gammaln(n+1) + np.sum(xlogy(x, p) - gammaln(x+1), axis=-1)
With your values:
import numpy as np
from scipy.special import xlogy, gammaln
x = np.array([3, 3, 0])
n = 6
p = np.array([2/3.0, 1/3.0, 0])
result = np.exp(gammaln(n+1) + np.sum(xlogy(x, p) - gammaln(x+1), axis=-1))
print(result)
>>>0.219478737997
I am trying to fit a model to some data. The independent variables are called A and B, and they are columns in a Pandas DataFrame. I am trying to fit with two parameters against y in the data frame.
Previously, with curve_fit from Scipy, I could do:
def fun(X, p1, p2):
A, B = X
return np.exp(p1*A) + p2*B
X = (df['A'].tolist(), df['B'].tolist())
popt, pcov = curve_fit(fun, X, df['y'].tolist())
But now, I'm using lmfit, where I cannot simply "pack" the independent variables like with curve_fit:
def fun(A, B, p1 = 1, p2 = 1):
return np.exp(p1*A) + p2*B
model = Model(fun, independent_vars=['A', 'B'])
How do I run model.fit() here? The FAQ is not really helpful—what do I have to flatten in the first place?
I created a complete, working example with two independent variables:
import pandas as pd
import numpy as np
from lmfit import Model
df = pd.DataFrame({
'A' : pd.Series([1, 1, 1, 2, 2, 2, 2]),
'B' : pd.Series([5, 4, 6, 6, 5, 6, 5]),
'target' : pd.Series([87.79, 40.89, 215.30, 238.65, 111.15, 238.65, 111.15])
})
def fun(A, B, p1 = 1, p2 = 1):
return p1 * np.exp(A) + p2 * np.exp(B)
model = Model(fun, independent_vars=['A', 'B'])
fit = model.fit(df['target'], A = df['A'], B = df['B'])
The trick is to specify all variables as keyword arguments in fit().
Firstly, creat a model with this function of multiple independent variables.
for example,
def random_func(x,y,a,b,c):
return a*x**3+b*y**2+c
Secondly, specify which ones are the independent variables in the formula.
for example,
from lmfit import Model
model = Model(random_func,independent_vars=['x','y'])
Thirdly, set params for the model
for example,
model.set_param_hint('a',value=2)
model.set_param_hint('b',value=3)
model.set_param_hint('c',value=4)
finally, set your x-axis values, as well as y-axis. And do the fit
Like this,
x = np.arange(0,2,0.1)
y = np.arange(0,2,0.1)
z = np.loadtxt('filename')
A direct fit actually does not work well. The 2D data array has to be flattened into 1D array, as well as the coordinates. For example, let's leave the model as it is. We need to create new 1D coordinates arrays.
x1d = []
y1d = []
for i in x:
for j in y:
x1d = x1d.append(i)
y1d = y1d.append(j)
z1d = z.flatten_data()
result = model.fit(z1d, x = x1d, y = y1d)
I can't find an explanation in the documentation or anywhere online. What does 'slinear' stand for and what does it do?
Looking at the source of scipy/interpolate/interpolate.py,
slinear is a spline of order 1
if kind in ['zero', 'slinear', 'quadratic', 'cubic']:
order = {'nearest': 0, 'zero': 0,'slinear': 1,
'quadratic': 2, 'cubic': 3}[kind]
kind = 'spline'
...
if kind in ('linear', 'nearest'):
# Make a "view" of the y array that is rotated to the interpolation
# axis.
minval = 2
if kind == 'linear':
self._call = self._call_linear
elif kind == 'nearest':
self.x_bds = (x[1:] + x[:-1]) / 2.0
self._call = self._call_nearest
else:
minval = order + 1
self._call = self._call_spline
self._spline = splmake(x, y, order=order)
Since the docs for splmake state:
def splmake(xk, yk, order=3, kind='smoothest', conds=None):
"""
Return a representation of a spline given data-points at internal knots
...