def least_squares(self, **kwargs): """ Returns: res: dict of fitted values predicted: array of float """ mini = Minimizer(self.obj_func, self.params, fcn_args=(self.x, self.y)) self.out = mini.least_squares(self.params) predicted = self.y + self.out.residual res = {} for k in self.out.__dict__['params']: res[self.out.params[k].name] = self.out.params[k].value return res, predicted
def test_bounds(): if not HAS_LEAST_SQUARES: raise nose.SkipTest p_true = Parameters() p_true.add('amp', value=14.0) p_true.add('period', value=5.4321) p_true.add('shift', value=0.12345) p_true.add('decay', value=0.01000) def residual(pars, x, data=None): amp = pars['amp'] per = pars['period'] shift = pars['shift'] decay = pars['decay'] if abs(shift) > pi/2: shift = shift - sign(shift)*pi model = amp*sin(shift + x/per) * exp(-x*x*decay*decay) if data is None: return model return (model - data) n = 1500 xmin = 0. xmax = 250.0 random.seed(0) noise = random.normal(scale=2.80, size=n) x = linspace(xmin, xmax, n) data = residual(p_true, x) + noise fit_params = Parameters() fit_params.add('amp', value=13.0, max=20, min=0.0) fit_params.add('period', value=2, max=10) fit_params.add('shift', value=0.0, max=pi/2., min=-pi/2.) fit_params.add('decay', value=0.02, max=0.10, min=0.00) min = Minimizer(residual, fit_params, (x, data)) out = min.least_squares() assert(out.nfev > 10) assert(out.nfree > 50) assert(out.chisqr > 1.0) print(fit_report(out, show_correl=True, modelpars=p_true)) assert_paramval(out.params['decay'], 0.01, tol=1.e-2) assert_paramval(out.params['shift'], 0.123, tol=1.e-2)
def solve_general(self, nh, delfstar, overlayer, prop_guess={}): ''' solve the property of a single test. nh: list of int delfstar: dict {harm(int): complex, ...} overlayer: dicr e.g.: {'drho': 0, 'grho_rh': 0, 'phi': 0} return drho, grho_rh, phi, dlam_rh, err ''' # input variables - this is helpfulf for the error analysis # define sensibly names partial derivatives for further use deriv = {} err = {} err_names = ['drho', 'grho_rh', 'phi'] # first pass at solution comes from rh and rd rd_exp = self.rdexp(nh, delfstar) # nh[2] rh_exp = self.rhexp(nh, delfstar) # nh[0], nh[1] logger.info('rd_exp', rd_exp) logger.info('rh_exp', rh_exp) n1 = nh[0] n2 = nh[1] n3 = nh[2] # solve the problem if ~np.isnan(rd_exp) or ~np.isnan(rh_exp): logger.info('rd_exp, rh_exp is not nan') # TODO change here for the model selection if prop_guess: # value{'drho', 'grho_rh', 'phi'} dlam_rh, phi = self.guess_from_props(**prop_guess) elif rd_exp > 0.5: dlam_rh, phi = self.bulk_guess(delfstar) else: dlam_rh, phi = self.thinfilm_guess(delfstar) logger.info('dlam_rh', dlam_rh) logger.info('phi', phi) if fit_method == 'lmfit': params1 = Parameters() params1.add('dlam_rh', value=dlam_rh, min=dlam_rh_range[0], max=dlam_rh_range[1]) params1.add('phi', value=phi, min=phi_range[0], max=phi_range[1]) def residual1(params, rh_exp, rd_exp): # dlam_rh = params['dlam_rh'].value # phi = params['phi'].value return [ self.rhcalc(nh, dlam_rh, phi) - rh_exp, self.rdcalc(nh, dlam_rh, phi) - rd_exp ] mini = Minimizer( residual1, params1, fcn_args=(rh_exp, rd_exp), # nan_policy='omit', ) soln1 = mini.leastsq( # xtol=1e-7, # ftol=1e-7, ) print(fit_report(soln1)) #testprint logger.info('success', soln1.success) logger.info('message', soln1.message) logger.info('lmdif_message', soln1.lmdif_message) dlam_rh = soln1.params.get('dlam_rh').value phi = soln1.params.get('phi').value drho = self.drho(n1, delfstar, dlam_rh, phi) grho_rh = self.grho_from_dlam(self.rh, drho, dlam_rh, phi) else: # scipy lb = np.array([dlam_rh_range[0], phi_range[0]]) # lower bounds on dlam3 and phi ub = np.array([dlam_rh_range[1], phi_range[1]]) # upper bonds on dlam3 and phi def ftosolve(x): return [ self.rhcalc(nh, x[0], x[1]) - rh_exp, self.rdcalc(nh, x[0], x[1]) - rd_exp ] x0 = np.array([dlam_rh, phi]) logger.info(x0) soln1 = least_squares(ftosolve, x0, bounds=(lb, ub)) logger.info(soln1['x']) dlam_rh = soln1['x'][0] phi = soln1['x'][1] drho = self.drho(n1, delfstar, dlam_rh, phi) grho_rh = self.grho_from_dlam(self.rh, drho, dlam_rh, phi) logger.info('solution of 1st solving:') logger.info('dlam_rh', dlam_rh) logger.info('phi', phi) logger.info('drho', phi) logger.info('grho_rh', grho_rh) # we solve it again to get the Jacobian with respect to our actual if drho_range[0] <= drho <= drho_range[1] and grho_rh_range[ 0] <= grho_rh <= grho_rh_range[1] and phi_range[ 0] <= phi <= phi_range[1]: logger.info('1st solution in range') if fit_method == 'lmfit': params2 = Parameters() params2.add('drho', value=dlam_rh, min=drho_range[0], max=drho_range[1]) params2.add('grho_rh', value=grho_rh, min=grho_rh_range[0], max=grho_rh_range[1]) params2.add('phi', value=phi, min=phi_range[0], max=phi_range[1]) def residual2(params, delfstar, overlayer, n1, n2, n3): drho = params['drho'].value grho_rh = params['grho_rh'].value phi = params['phi'].value return ([ np.real(delfstar[n1]) - np.real( self.delfstarcalc(n1, drho, grho_rh, phi, overlayer)), np.real(delfstar[n2]) - np.real( self.delfstarcalc(n2, drho, grho_rh, phi, overlayer)), np.imag(delfstar[n3]) - np.imag( self.delfstarcalc(n3, drho, grho_rh, phi, overlayer)) ]) mini = Minimizer( residual2, params2, fcn_args=(delfstar, overlayer, n1, n2, n3), # nan_policy='omit', ) soln2 = mini.least_squares( # xtol=1e-7, # ftol=1e-7, ) logger.info(soln2.params.keys()) logger.info(soln2.params['drho']) print(fit_report(soln2)) print('success', soln2.success) print('message', soln2.message) print('lmdif_message', soln1.lmdif_message) # put the input uncertainties into a 3 element vector delfstar_err = np.zeros(3) delfstar_err[0] = np.real(self.fstar_err_calc( delfstar[n1])) delfstar_err[1] = np.real(self.fstar_err_calc( delfstar[n2])) delfstar_err[2] = np.imag(self.fstar_err_calc( delfstar[n3])) # initialize the uncertainties # recalculate solution to give the uncertainty, if solution is viable drho = soln2.params.get('drho').value grho_rh = soln2.params.get('grho_rh').value phi = soln2.params.get('phi').value dlam_rh = self.d_lamcalc(self.rh, drho, grho_rh, phi) jac = soln2.params.get('jac') #TODO ??? logger.info('jac', jac) jac_inv = np.linalg.inv(jac) for i, k in enumerate(err_names): deriv[k] = { 0: jac_inv[i, 0], 1: jac_inv[i, 1], 2: jac_inv[i, 2] } err[k] = ((jac_inv[i, 0] * delfstar_err[0])**2 + (jac_inv[i, 1] * delfstar_err[1])**2 + (jac_inv[i, 2] * delfstar_err[2])**2)**0.5 else: # scipy x0 = np.array([drho, grho_rh, phi]) lb = np.array( [drho_range[0], grho_rh_range[0], phi_range[0]]) # lower bounds drho, grho3, phi ub = np.array( [drho_range[1], grho_rh_range[1], phi_range[1]]) # upper bounds drho, grho3, phi def ftosolve2(x): return ([ np.real(delfstar[n1]) - np.real( self.delfstarcalc(n1, x[0], x[1], x[2], overlayer)), np.real(delfstar[n2]) - np.real( self.delfstarcalc(n2, x[0], x[1], x[2], overlayer)), np.imag(delfstar[n3]) - np.imag( self.delfstarcalc(n3, x[0], x[1], x[2], overlayer)) ]) # put the input uncertainties into a 3 element vector delfstar_err = np.zeros(3) delfstar_err[0] = np.real(self.fstar_err_calc( delfstar[n1])) delfstar_err[1] = np.real(self.fstar_err_calc( delfstar[n2])) delfstar_err[2] = np.imag(self.fstar_err_calc( delfstar[n3])) # recalculate solution to give the uncertainty, if solution is viable soln2 = least_squares(ftosolve2, x0, bounds=(lb, ub)) drho = soln2['x'][0] grho_rh = soln2['x'][1] phi = soln2['x'][2] dlam_rh = self.d_lamcalc(self.rh, drho, grho_rh, phi) jac = soln2['jac'] logger.info('jac', jac) jac_inv = np.linalg.inv(jac) logger.info('jac_inv', jac_inv) for i, k in enumerate(err_names): deriv[k] = { 0: jac_inv[i, 0], 1: jac_inv[i, 1], 2: jac_inv[i, 2] } err[k] = ((jac_inv[i, 0] * delfstar_err[0])**2 + (jac_inv[i, 1] * delfstar_err[1])**2 + (jac_inv[i, 2] * delfstar_err[2])**2)**0.5 if np.isnan(rd_exp) or np.isnan( rh_exp) or not deriv or not err: # failed to solve the problem print('2nd solving failed') # assign the default value first drho = np.nan grho_rh = np.nan phi = np.nan dlam_rh = np.nan for k in err_names: err[k] = np.nan logger.info('drho', drho) logger.info('grho_rh', grho_rh) logger.info('phi', phi) logger.info('dlam_rh', phi) logger.info('err', err) return drho, grho_rh, phi, dlam_rh, err