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
Example #2
0
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)
Example #3
0
    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