def chained_lsqfit(
        self, data=None, pdata=None, prior=None, p0=None, add_priornoise=None,
        **kargs
        ):
        """ Compute chained least-squares fit of models to data.

        In a chained fit to models ``[s1, s2, ...]``, the models are fit one
        at a time, with the fit output from one being fed into the prior for
        the next. This can be much faster than  fitting the models together,
        simultaneously. The final result comes from the last fit in the chain,
        and includes parameters from all of the models.

        The most general chain has the structure ``[s1, s2, s3 ...]``
        where each ``sn`` is one of:

            1) A model (derived from :class:`multifitter.MultiFitterModel`).

            2) A tuple ``(m1, m2, m3)`` of models, to be fit together in
                a single fit (i.e., simultaneously). Simultaneous fits
                are useful for closely related models.

            3) A list ``[p1, p2, p3 ...]`` where each ``pn`` is either
                a model or a tuple of models (see #2). The ``pn`` are fit
                separately: the fit output from one fit is *not* fed into the
                prior of the next (i.e., the fits are effectively in
                parallel). Results from the separate fits are averaged at the
                end to provide a single composite result for the collection of
                fits. Parallel fits are effective (and fast) when the
                different fits have few or no fit parameters in common.

            4) A dictionary that (temporarily) resets default values for
                fitter keywords. The new values, specified in the dictionary,
                apply to subsequent fits in the chain. Any number of such
                dictionaries can be included in the model chain.


        Fit results are returned in a :class:`lsqfit.MultiFitter.chained_fit`
        object ``fit``, which is very similar to a :class:`nonlinear_fit`
        object (see documentation for more information). Object ``fit`` has an
        extra attribute ``fit.chained_fits`` which is an ordered dictionary
        containing fit results for each link in the chain of fits, indexed by
        fit names built from the corresponding data tags.

        To list results from all of the fits in the chain, use ::

            print(fit.formatall())

        This method has optional keyword arguments ``maxline``,
        ``pstyle``, and ``nline``; see the documentation for
        :meth:`lsqfit.nonlinear_fit.format` for more
        information.

        To view plots of each fit use

            fit.show_plots()

        This method has optional keyword arguments ``save`` and ``view``;
        see documentation for :class:`lsqfit.MultiFitter.show_plots`
        for more information. Plotting requires module :mod:`matplotlib`.

        To bootstrap a fit, use ``fit.bootstrapped_fit_iter(...)``;
        see :meth:`lsqfit.nonlinear_fit.bootstrapped_fit_iter` for more
        information.

        Args:
            data: Input data. One of ``data`` or ``pdata`` must be
                specified but not both. ``pdata`` is obtained from ``data``
                by collecting the output from ``m.builddata(data)``
                for each model ``m`` and storing it in a dictionary
                with key ``m.datatag``.
            pdata: Input data that has been processed by the
                models using :meth:`MultiFitter.process_data` or
                :meth:`MultiFitter.process_dataset`. One of
                ``data`` or ``pdata`` must be  specified but not both.
            prior: Bayesian prior for fit parameters used by the models.
            p0: Dictionary , indexed by parameter labels, containing
                initial values for the parameters in the fit. Setting
                ``p0=None`` implies that initial values are extracted from the
                prior. Setting ``p0="filename"`` causes the fitter to look in
                the file with name ``"filename"`` for initial values and to
                write out best-fit parameter values after the fit (for the
                next call to ``self.lsqfit()``).
            kargs: Arguments that override parameters specified when
                the :class:`MultiFitter` was created. Can also include
                additional arguments to be passed through to
                the :mod:`lsqfit` fitter.
        """
        if prior is None:
            raise ValueError('no prior')
        if add_priornoise:
            # intercept at top level so same treatment for all fits
            prior = prior + (gvar.sample(prior) - gvar.mean(prior))
        kargs, oldargs = self.set(**kargs)

        # parameters for bootstrap (see below)
        fitter_args_kargs = (
            self.chained_lsqfit,
            dict(data=data, prior=prior, pdata=pdata, models=self.models),
            dict(kargs),
            )

        # local copy of prior
        if self.fast:
            prior = self.buildprior(prior)
        else:
            prior = gvar.BufferDict(prior)

        # execute tasks in self.tasklist
        chained_fits = collections.OrderedDict()
        all_fnames = []
        all_fitp = []
        for tasktype, taskdata in self.tasklist:
            if tasktype == 'fit':
                fitter = self.__class__(models=taskdata, **kargs)
                fit = fitter.lsqfit(
                    data=data, pdata=pdata, prior=prior, p0=p0
                    )
                fname = list(fit.chained_fits.keys())[0]
                if fname in chained_fits:
                    raise ValueError('duplicate fits in chain: ' + str(fname))
                elif fname[:5] == 'wavg(' and fname[-1] == ')':
                    raise ValueError('bad fit name: ' + fname)
                else:
                    all_fnames.append(fname)
                    chained_fits[fname] = fit
                    all_fitp.append(fit.p)
            elif tasktype == 'update-prior':
                lastfit = chained_fits[all_fnames[-1]]
                lastfit_p = lastfit.p
                for k in lastfit_p:
                    idx = tuple(
                        slice(None, i) for i in numpy.shape(lastfit.p[k])
                        )
                    if idx != ():
                        prior[k][idx] = lastfit.p[k]
                    else:
                        prior[k] = lastfit.p[k]
            elif tasktype == 'wavg':
                    nlist = all_fnames[-taskdata:]
                    plist = [chained_fits[k].p for k in nlist]
                    fit = lsqfit.wavg(plist, **self.wavg_kargs).fit
                    fname = self.fitname('wavg({})'.format(','.join(nlist)))
                    all_fnames.append(fname)
                    chained_fits[fname] = fit
            elif tasktype == 'update-kargs':
                kargs.update(taskdata)
            else:
                raise RuntimeError('unknown task: ' + tasktype)

        if self.fast and self.wavg_all:
            fit = lsqfit.wavg(all_fitp, **self.wavg_kargs).fit
            fname = self.fitname('wavg(all)')
            chained_fits[fname] = fit
            prior = fit.p

        # build output class
        self.fit = chained_nonlinear_fit(
            p=prior, chained_fits=chained_fits,
            multifitter=self, prior=fitter_args_kargs[1]['prior'],
            )

        # add bootstrap method
        fitter_args_kargs[1]['p0'] = self.fit.pmean
        def _bstrap_iter(
            n=None, datalist=None, pdatalist=None, **kargs
            ):
            return MultiFitter._bootstrapped_fit_iter(
                fitter_args_kargs,
                n=n, datalist=datalist, pdatalist=pdatalist, **kargs
                )
        self.fit.bootstrapped_fit_iter = _bstrap_iter

        # restore default keywords
        self.set(**oldargs)
        return self.fit
Compute chained least-squares fit of models to data.

        In a chained fit to models ``[s1, s2, ...]``, the models are fit one
        at a time, with the fit output from one being fed into the prior for
        the next. This can be much faster than  fitting the models together,
        simultaneously. The final result comes from the last fit in the chain,
        and includes parameters from all of the models.

        The most general chain has the structure ``[s1, s2, s3 ...]``
        where each ``sn`` is one of:

            1) A model (derived from :class:`multifitter.MultiFitterModel`).

            2) A tuple ``(m1, m2, m3)`` of models, to be fit together in
                a single fit (i.e., simultaneously). Simultaneous fits
                are useful for closely related models.

            3) A list ``[p1, p2, p3 ...]`` where each ``pn`` is either
                a model or a tuple of models (see #2). The ``pn`` are fit
                separately: the fit output from one fit is *not* fed into the
                prior of the next (i.e., the fits are effectively in
                parallel). Results from the separate fits are averaged at the
                end to provide a single composite result for the collection of
                fits. Parallel fits are effective (and fast) when the
                different fits have few or no fit parameters in common.

            4) A dictionary that (temporarily) resets default values for
                fitter keywords. The new values, specified in the dictionary,
                apply to subsequent fits in the chain. Any number of such
                dictionaries can be included in the model chain.


        Fit results are returned in a :class:`lsqfit.MultiFitter.chained_fit`
        object ``fit``, which is very similar to a :class:`nonlinear_fit`
        object (see documentation for more information). Object ``fit`` has an
        extra attribute ``fit.chained_fits`` which is an ordered dictionary
        containing fit results for each link in the chain of fits, indexed by
        fit names built from the corresponding data tags.

        To list results from all of the fits in the chain, use ::

            print(fit.formatall())

        This method has optional keyword arguments ``maxline``,
        ``pstyle``, and ``nline``; see the documentation for
        :meth:`lsqfit.nonlinear_fit.format` for more
        information.

        To view plots of each fit use

            fit.show_plots()

        This method has optional keyword arguments ``save`` and ``view``;
        see documentation for :class:`lsqfit.MultiFitter.show_plots`
        for more information. Plotting requires module :mod:`matplotlib`.

        To bootstrap a fit, use ``fit.bootstrapped_fit_iter(...)``;
        see :meth:`lsqfit.nonlinear_fit.bootstrapped_fit_iter` for more
        information.

        Args:
            data: Input data. One of ``data`` or ``pdata`` must be
                specified but not both. ``pdata`` is obtained from ``data``
                by collecting the output from ``m.builddata(data)``
                for each model ``m`` and storing it in a dictionary
                with key ``m.datatag``.
            pdata: Input data that has been processed by the
                models using :meth:`MultiFitter.process_data` or
                :meth:`MultiFitter.process_dataset`. One of
                ``data`` or ``pdata`` must be  specified but not both.
            prior: Bayesian prior for fit parameters used by the models.
            p0: Dictionary , indexed by parameter labels, containing
                initial values for the parameters in the fit. Setting
                ``p0=None`` implies that initial values are extracted from the
                prior. Setting ``p0="filename"`` causes the fitter to look in
                the file with name ``"filename"`` for initial values and to
                write out best-fit parameter values after the fit (for the
                next call to ``self.lsqfit()``).
            kargs: Arguments that override parameters specified when
                the :class:`MultiFitter` was created. Can also include
                additional arguments to be passed through to
                the :mod:`lsqfit` fitter.