Exemple #1
0
    def __fromfile(self, filename):
        """a private method to deduce and load a filename into a matrix object

        Parameters:
        ----------
            filename (str) : the name of the file
        Returns:
        -------
            mat (or cov) object
        """
        assert os.path.exists(filename),"LinearAnalysis.__fromfile(): " +\
                                        "file not found:" + filename
        ext = filename.split('.')[-1].lower()
        if ext in ["jco", "jcb"]:
            self.log("loading jco: "+filename)
            m = Jco.from_binary(filename)
            self.log("loading jco: "+filename)
        elif ext in ["mat","vec"]:
            self.log("loading ascii: "+filename)
            m = Matrix.from_ascii(filename)
            self.log("loading ascii: "+filename)
        elif ext in ["cov"]:
            self.log("loading cov: "+filename)
            m = Cov.from_ascii(filename)
            self.log("loading cov: "+filename)
        elif ext in["unc"]:
            self.log("loading unc: "+filename)
            m = Cov.from_uncfile(filename)
            self.log("loading unc: "+filename)
        else:
            raise Exception("linear_analysis.__fromfile(): unrecognized" +
                            " filename extension:" + str(ext))
        return m
Exemple #2
0
    def __fromfile(self, filename):
        """a private method to deduce and load a filename into a matrix object

        Parameters:
        ----------
            filename (str) : the name of the file
        Returns:
        -------
            mat (or cov) object
        """
        assert os.path.exists(filename),"LinearAnalysis.__fromfile(): " +\
                                        "file not found:" + filename
        ext = filename.split('.')[-1].lower()
        if ext in ["jco", "jcb"]:
            self.log("loading jco: " + filename)
            m = Jco.from_binary(filename)
            self.log("loading jco: " + filename)
        elif ext in ["mat", "vec"]:
            self.log("loading ascii: " + filename)
            m = Matrix.from_ascii(filename)
            self.log("loading ascii: " + filename)
        elif ext in ["cov"]:
            self.log("loading cov: " + filename)
            m = Cov.from_ascii(filename)
            self.log("loading cov: " + filename)
        elif ext in ["unc"]:
            self.log("loading unc: " + filename)
            m = Cov.from_uncfile(filename)
            self.log("loading unc: " + filename)
        else:
            raise Exception("linear_analysis.__fromfile(): unrecognized" +
                            " filename extension:" + str(ext))
        return m
Exemple #3
0
 def __load_parcov(self):
     """private method to set the parcov attribute from:
             a pest control file (parameter bounds)
             a pst object
             a matrix object
             an uncert file
             an ascii matrix file
     """
     # if the parcov arg was not passed but the pst arg was,
     # reset and use parbounds to build parcov
     if not self.parcov_arg:
         if self.pst_arg:
             self.parcov_arg = self.pst_arg
         else:
             raise Exception("linear_analysis.__load_parcov(): " +
                             "parcov_arg is None")
     if isinstance(self.parcov_arg, Matrix):
         self.__parcov = self.parcov_arg
         return
     if isinstance(self.parcov_arg, np.ndarray):
         # if the passed array is a vector,
         # then assume it is the diagonal of the parcov matrix
         if len(self.parcov_arg.shape) == 1:
             assert self.parcov_arg.shape[0] == self.jco.shape[1]
             isdiagonal = True
         else:
             assert self.parcov_arg.shape[0] == self.jco.shape[1]
             assert self.parcov_arg.shape[1] == self.jco.shape[1]
             isdiagonal = False
         self.logger.warn("linear_analysis.__load_parcov(): " +
                          "instantiating parcov from ndarray, can't " +
                          "verify parameters alignment with jco")
         self.__parcov = Matrix(x=self.parcov_arg,
                                isdiagonal=isdiagonal,
                                row_names=self.jco.col_names,
                                col_names=self.jco.col_names)
     self.log("loading parcov")
     if isinstance(self.parcov_arg, str):
         # if the arg is a string ending with "pst"
         # then load parcov from parbounds
         if self.parcov_arg.lower().endswith(".pst"):
             self.__parcov = Cov.from_parbounds(
                 self.parcov_arg,
                 sigma_range=self.sigma_range,
                 scale_offset=self.scale_offset)
         else:
             self.__parcov = self.__fromfile(self.parcov_arg, astype=Cov)
     # if the arg is a pst object
     elif isinstance(self.parcov_arg, Pst):
         self.__parcov = Cov.from_parameter_data(
             self.parcov_arg,
             sigma_range=self.sigma_range,
             scale_offset=self.scale_offset)
     else:
         raise Exception("linear_analysis.__load_parcov(): " +
                         "parcov_arg must be a " +
                         "matrix object or a file name: " +
                         str(self.parcov_arg))
     self.log("loading parcov")
Exemple #4
0
 def __load_parcov(self):
     """private: set the parcov attribute from:
             a pest control file (parameter bounds)
             a pst object
             a matrix object
             an uncert file
             an ascii matrix file
     """
     # if the parcov arg was not passed but the pst arg was,
     # reset and use parbounds to build parcov
     if not self.parcov_arg:
         if self.pst_arg:
             self.parcov_arg = self.pst_arg
         else:
             raise Exception("linear_analysis.__load_parcov(): " +
                             "parcov_arg is None")
     if isinstance(self.parcov_arg, Matrix):
         self.__parcov = self.parcov_arg
         return
     if isinstance(self.parcov_arg, np.ndarray):
         # if the passed array is a vector,
         # then assume it is the diagonal of the parcov matrix
         if len(self.parcov_arg.shape) == 1:
             assert self.parcov_arg.shape[0] == self.jco.shape[1]
             isdiagonal = True
         else:
             assert self.parcov_arg.shape[0] == self.jco.shape[1]
             assert self.parcov_arg.shape[1] == self.jco.shape[1]
             isdiagonal = False
         self.logger.warn("linear_analysis.__load_parcov(): " +
                          "instantiating parcov from ndarray, can't " +
                          "verify parameters alignment with jco")
         self.__parcov = Matrix(x=self.parcov_arg,
                                      isdiagonal=isdiagonal,
                                      row_names=self.jco.col_names,
                                      col_names=self.jco.col_names)
     self.log("loading parcov")
     if isinstance(self.parcov_arg,str):
         # if the arg is a string ending with "pst"
         # then load parcov from parbounds
         if self.parcov_arg.lower().endswith(".pst"):
             self.__parcov = Cov.from_parbounds(self.parcov_arg)
         else:
             self.__parcov = self.__fromfile(self.parcov_arg)
     # if the arg is a pst object
     elif isinstance(self.parcov_arg,Pst):
         self.__parcov = Cov.from_parameter_data(self.parcov_arg)
     else:
         raise Exception("linear_analysis.__load_parcov(): " +
                         "parcov_arg must be a " +
                         "matrix object or a file name: " +
                         str(self.parcov_arg))
     self.log("loading parcov")
Exemple #5
0
 def __load_obscov(self):
     """private method to set the obscov attribute from:
             a pest control file (observation weights)
             a pst object
             a matrix object
             an uncert file
             an ascii matrix file
     """
     # if the obscov arg is None, but the pst arg is not None,
     # reset and load from obs weights
     if not self.obscov_arg:
         if self.pst_arg:
             self.obscov_arg = self.pst_arg
         else:
             raise Exception("linear_analysis.__load_obscov(): " +
                             "obscov_arg is None")
     if isinstance(self.obscov_arg, Matrix):
         self.__obscov = self.obscov_arg
         return
     if isinstance(self.obscov_arg, np.ndarray):
         # if the ndarray arg is a vector,
         # assume it is the diagonal of the obscov matrix
         if len(self.obscov_arg.shape) == 1:
             assert self.obscov_arg.shape[0] == self.jco.shape[1]
             isdiagonal = True
         else:
             assert self.obscov_arg.shape[0] == self.jco.shape[0]
             assert self.obscov_arg.shape[1] == self.jco.shape[0]
             isdiagonal = False
         self.logger.warn("linear_analysis.__load_obscov(): " +
                          "instantiating obscov from ndarray,  " +
                          "can't verify observation alignment with jco")
         self.__obscov = Matrix(
             x=self.obscov_arg,
             isdiagonal=isdiagonal,
             row_names=self.jco.row_names,
             col_names=self.jco.row_names,
         )
     self.log("loading obscov")
     if isinstance(self.obscov_arg, str):
         if self.obscov_arg.lower().endswith(".pst"):
             self.__obscov = Cov.from_obsweights(self.obscov_arg)
         else:
             self.__obscov = self.__fromfile(self.obscov_arg, astype=Cov)
     elif isinstance(self.obscov_arg, Pst):
         self.__obscov = Cov.from_observation_data(self.obscov_arg)
     else:
         raise Exception("linear_analysis.__load_obscov(): " +
                         "obscov_arg must be a " +
                         "matrix object or a file name: " +
                         str(self.obscov_arg))
     self.log("loading obscov")
Exemple #6
0
 def __load_obscov(self):
     """private: method to set the obscov attribute from:
             a pest control file (observation weights)
             a pst object
             a matrix object
             an uncert file
             an ascii matrix file
     """
     # if the obscov arg is None, but the pst arg is not None,
     # reset and load from obs weights
     if not self.obscov_arg:
         if self.pst_arg:
             self.obscov_arg = self.pst_arg
         else:
             raise Exception("linear_analysis.__load_obscov(): " +
                             "obscov_arg is None")
     if isinstance(self.obscov_arg, Matrix):
         self.__obscov = self.obscov_arg
         return
     if isinstance(self.obscov_arg,np.ndarray):
         # if the ndarray arg is a vector,
         # assume it is the diagonal of the obscov matrix
         if len(self.obscov_arg.shape) == 1:
             assert self.parcov_arg.shape[0] == self.jco.shape[1]
             isdiagonal = True
         else:
             assert self.obscov_arg.shape[0] == self.jco.shape[0]
             assert self.obscov_arg.shape[1] == self.jco.shape[0]
             isdiagonal = False
         self.logger.warn("linear_analysis.__load_obscov(): " +
                          "instantiating obscov from ndarray,  " +
                          "can't verify observation alignment with jco")
         self.__parcov = Matrix(x=self.obscov_arg,
                                      isdiagonal=isdiagonal,
                                      row_names=self.jco.row_names,
                                      col_names=self.jco.row_names)
     self.log("loading obscov")
     if isinstance(self.obscov_arg, str):
         if self.obscov_arg.lower().endswith(".pst"):
             self.__obscov = Cov()
             self.__obscov.from_obsweights(self.obscov_arg)
         else:
             self.__obscov = self.__fromfile(self.obscov_arg)
     elif isinstance(self.obscov_arg, Pst):
         self.__obscov = Cov()
         self.__obscov.from_observation_data(self.obscov_arg)
     else:
         raise Exception("linear_analysis.__load_obscov(): " +
                         "obscov_arg must be a " +
                         "matrix object or a file name: " +
                         str(self.obscov_arg))
     self.log("loading obscov")
Exemple #7
0
 def __load_omitted_parcov(self):
     """private: set the omitted_parcov attribute"""
     if self.omitted_parcov_arg is None and self.omitted_par_arg is None:
         raise Exception(
             "ErrVar.__load_omitted_parcov: " + "both omitted args are None"
         )
     # try to set omitted_parcov by extracting from base parcov
     if self.omitted_parcov_arg is None and self.omitted_par_arg is not None:
         # check to see if omitted par names are in parcov
         found = True
         for par_name in self.omitted_jco.col_names:
             if par_name not in self.parcov.col_names:
                 found = False
                 break
         if found:
             # need to access attribute directly, not view of attribute
             self.__omitted_parcov = self._LinearAnalysis__parcov.extract(
                 row_names=self.omitted_jco.col_names
             )
         else:
             self.logger.warn(
                 "ErrVar.__load_omitted_parun: "
                 + "no omitted parcov arg passed: "
                 + "setting omitted parcov as identity Matrix"
             )
             self.__omitted_parcov = Cov(
                 x=np.ones(self.omitted_jco.shape[1]),
                 names=self.omitted_jco.col_names,
                 isdiagonal=True,
             )
     elif self.omitted_parcov_arg is not None:
         raise NotImplementedError()
Exemple #8
0
class LinearAnalysis(object):
    """ the super class for linear analysis.  Can be used for prior analyses
        only.  The derived types (schur and errvar) are for posterior analyses
        this class tries hard to not load items until they are needed
        all arguments are optional

    Parameters:
    ----------
        jco ([enumerable of] [string,ndarray,matrix objects]) : jacobian
        pst (pst object) : the pest control file object
        parcov ([enumerable of] [string,ndarray,matrix objects]) :
            parameter covariance matrix
        obscov ([enumerable of] [string,ndarray,matrix objects]):
            observation noise covariance matrix
        predictions ([enumerable of] [string,ndarray,matrix objects]) :
            prediction sensitivity vectors
        ref_var (float) : reference variance
        verbose (either bool or string) : controls log file / screen output
    Notes:
        the class makes heavy use of property decorator to encapsulate
        private attributes
    """
    def __init__(self, jco=None, pst=None, parcov=None, obscov=None,
                 predictions=None, ref_var=1.0, verbose=False,
                 resfile=False, forecasts=None,**kwargs):
        self.logger = logger(verbose)
        self.log = self.logger.log
        self.jco_arg = jco
        #if jco is None:
        self.__jco = jco
        if pst is None:
            if isinstance(jco, str):
                pst_case = jco.replace(".jco", ".pst").replace(".jcb",".pst")
                if os.path.exists(pst_case):
                    pst = pst_case
        self.pst_arg = pst
        if parcov is None and pst is not None:
            parcov = pst
        self.parcov_arg = parcov
        if obscov is None and pst is not None:
            obscov = pst
        self.obscov_arg = obscov
        self.ref_var = ref_var
        if forecasts is not None and predictions is not None:
            raise Exception("can't pass both forecasts and predictions")
        if forecasts is not None:
            predictions = forecasts
        self.prediction_arg = predictions

        #private attributes - access is through @decorated functions
        self.__pst = None
        self.__parcov = None
        self.__obscov = None
        self.__predictions = None
        self.__qhalf = None
        self.__qhalfx = None
        self.__xtqx = None
        self.__fehalf = None
        self.__prior_prediction = None

        self.log("pre-loading base components")
        if jco is not None:
            self.__load_jco()
        if pst is not None:
            self.__load_pst()
        if parcov is not None:
            self.__load_parcov()
        if obscov is not None:
            self.__load_obscov()

        if predictions is not None:
            self.__load_predictions()
        self.log("pre-loading base components")
        if len(kwargs.keys()) > 0:
            self.logger.warn("unused kwargs in type " +
                             str(self.__class__.__name__) +
                             " : " + str(kwargs))
            raise Exception("unused kwargs" +
                             " : " + str(kwargs))
        # automatically do some things that should be done
        self.log("dropping prior information")
        pi = None
        try:
            pi = self.pst.prior_information
        except:
            self.logger.warn("unable to access self.pst: can't tell if " +
                             " any prior information needs to be dropped.")
        if pi is not None:
            self.drop_prior_information()
        self.log("dropping prior information")


        if resfile != False:
            self.log("scaling obscov by residual phi components")
            try:
                self.adjust_obscov_resfile(resfile=resfile)
            except:
                self.logger.warn("unable to a find a residuals file for " +\
                                " scaling obscov")
                self.resfile = None
                self.res = None
            self.log("scaling obscov by residual phi components")

    def __fromfile(self, filename):
        """a private method to deduce and load a filename into a matrix object

        Parameters:
        ----------
            filename (str) : the name of the file
        Returns:
        -------
            mat (or cov) object
        """
        ext = filename.split('.')[-1].lower()
        if ext in ["jco", "jcb"]:
            self.log("loading jco: "+filename)
            m = Jco()
            m.from_binary(filename)
            self.log("loading jco: "+filename)
        elif ext in ["mat","vec"]:
            self.log("loading ascii: "+filename)
            m = Matrix()
            m.from_ascii(filename)
            self.log("loading ascii: "+filename)
        elif ext in ["cov"]:
            self.log("loading cov: "+filename)
            m = Cov()
            m.from_ascii(filename)
            self.log("loading cov: "+filename)
        elif ext in["unc"]:
            self.log("loading unc: "+filename)
            m = Cov()
            m.from_uncfile(filename)
            self.log("loading unc: "+filename)
        else:
            raise Exception("linear_analysis.__fromfile(): unrecognized" +
                            " filename extension:" + str(ext))
        return m


    def __load_pst(self):
        """private: set the pst attribute
        Parameters:
        ----------
            None
        Returns:
        -------
            None
        """
        if self.pst_arg is None:
            return None
        if isinstance(self.pst_arg, Pst):
            self.__pst = self.pst_arg
            return self.pst
        else:
            try:
                self.log("loading pst: " + str(self.pst_arg))
                self.__pst = Pst(self.pst_arg)
                self.log("loading pst: " + str(self.pst_arg))
                return self.pst
            except Exception as e:
                raise Exception("linear_analysis.__load_pst(): error loading"+\
                                " pest control from argument: " +
                                str(self.pst_arg) + '\n->' + str(e))


    def __load_jco(self):
        """private :set the jco attribute from a file or a matrix object
        Parameters:
        ----------
            None
        Returns:
        -------
            None
        """
        if self.jco_arg is None:
            return None
            #raise Exception("linear_analysis.__load_jco(): jco_arg is None")
        if isinstance(self.jco_arg, Matrix):
            self.__jco = self.jco_arg
        elif isinstance(self.jco_arg, str):
            self.__jco = self.__fromfile(self.jco_arg)
        else:
            raise Exception("linear_analysis.__load_jco(): jco_arg must " +
                            "be a matrix object or a file name: " +
                            str(self.jco_arg))


    def __load_parcov(self):
        """private: set the parcov attribute from:
                a pest control file (parameter bounds)
                a pst object
                a matrix object
                an uncert file
                an ascii matrix file
        """
        # if the parcov arg was not passed but the pst arg was,
        # reset and use parbounds to build parcov
        if not self.parcov_arg:
            if self.pst_arg:
                self.parcov_arg = self.pst_arg
            else:
                raise Exception("linear_analysis.__load_parcov(): " +
                                "parcov_arg is None")
        if isinstance(self.parcov_arg, Matrix):
            self.__parcov = self.parcov_arg
            return
        if isinstance(self.parcov_arg, np.ndarray):
            # if the passed array is a vector,
            # then assume it is the diagonal of the parcov matrix
            if len(self.parcov_arg.shape) == 1:
                assert self.parcov_arg.shape[0] == self.jco.shape[1]
                isdiagonal = True
            else:
                assert self.parcov_arg.shape[0] == self.jco.shape[1]
                assert self.parcov_arg.shape[1] == self.jco.shape[1]
                isdiagonal = False
            self.logger.warn("linear_analysis.__load_parcov(): " +
                             "instantiating parcov from ndarray, can't " +
                             "verify parameters alignment with jco")
            self.__parcov = Matrix(x=self.parcov_arg,
                                         isdiagonal=isdiagonal,
                                         row_names=self.jco.col_names,
                                         col_names=self.jco.col_names)
        self.log("loading parcov")
        if isinstance(self.parcov_arg,str):
            # if the arg is a string ending with "pst"
            # then load parcov from parbounds
            if self.parcov_arg.lower().endswith(".pst"):
                self.__parcov = Cov()

                self.__parcov.from_parbounds(self.parcov_arg)
            else:
                self.__parcov = self.__fromfile(self.parcov_arg)
        # if the arg is a pst object
        elif isinstance(self.parcov_arg,Pst):
            self.__parcov = Cov()
            self.__parcov.from_parameter_data(self.parcov_arg)
        else:
            raise Exception("linear_analysis.__load_parcov(): " +
                            "parcov_arg must be a " +
                            "matrix object or a file name: " +
                            str(self.parcov_arg))
        self.log("loading parcov")


    def __load_obscov(self):
        """private: method to set the obscov attribute from:
                a pest control file (observation weights)
                a pst object
                a matrix object
                an uncert file
                an ascii matrix file
        """
        # if the obscov arg is None, but the pst arg is not None,
        # reset and load from obs weights
        if not self.obscov_arg:
            if self.pst_arg:
                self.obscov_arg = self.pst_arg
            else:
                raise Exception("linear_analysis.__load_obscov(): " +
                                "obscov_arg is None")
        if isinstance(self.obscov_arg, Matrix):
            self.__obscov = self.obscov_arg
            return
        if isinstance(self.obscov_arg,np.ndarray):
            # if the ndarray arg is a vector,
            # assume it is the diagonal of the obscov matrix
            if len(self.obscov_arg.shape) == 1:
                assert self.parcov_arg.shape[0] == self.jco.shape[1]
                isdiagonal = True
            else:
                assert self.obscov_arg.shape[0] == self.jco.shape[0]
                assert self.obscov_arg.shape[1] == self.jco.shape[0]
                isdiagonal = False
            self.logger.warn("linear_analysis.__load_obscov(): " +
                             "instantiating obscov from ndarray,  " +
                             "can't verify observation alignment with jco")
            self.__parcov = Matrix(x=self.obscov_arg,
                                         isdiagonal=isdiagonal,
                                         row_names=self.jco.row_names,
                                         col_names=self.jco.row_names)
        self.log("loading obscov")
        if isinstance(self.obscov_arg, str):
            if self.obscov_arg.lower().endswith(".pst"):
                self.__obscov = Cov()
                self.__obscov.from_obsweights(self.obscov_arg)
            else:
                self.__obscov = self.__fromfile(self.obscov_arg)
        elif isinstance(self.obscov_arg, Pst):
            self.__obscov = Cov()
            self.__obscov.from_observation_data(self.obscov_arg)
        else:
            raise Exception("linear_analysis.__load_obscov(): " +
                            "obscov_arg must be a " +
                            "matrix object or a file name: " +
                            str(self.obscov_arg))
        self.log("loading obscov")


    def __load_predictions(self):
        """private: set the predictions attribute from:
                mixed list of row names, matrix files and ndarrays
                a single row name
                an ascii file
            can be none if only interested in parameters.

            linear_analysis.__predictions is stored as a list of column vectors

        """
        if self.prediction_arg is None:
            self.__predictions = None
            return
        self.log("loading forecasts")
        if not isinstance(self.prediction_arg, list):
            self.prediction_arg = [self.prediction_arg]

        row_names = []
        vecs = []
        for arg in self.prediction_arg:
            if isinstance(arg, Matrix):
                # a vector
                if arg.shape[1] == 1:
                    vecs.append(arg)
                else:
                    assert arg.shape[1] == self.jco.shape[1],\
                    "linear_analysis.__load_predictions(): " +\
                    "multi-prediction matrix(npred,npar) not aligned " +\
                    "with jco(nobs,npar): " + str(arg.shape) +\
                    ' ' + str(self.jco.shape)
                    for pred_name in arg.row_names:
                        vecs.append(arg.extract(row_names=pred_name).T)
            elif isinstance(arg, str):
                if arg.lower() in self.jco.row_names:
                    row_names.append(arg.lower())
                else:
                    pred_mat = self.__fromfile(arg)
                    # vector
                    if pred_mat.shape[1] == 1:
                        vecs.append(pred_mat)
                    else:
                        for pred_name in pred_mat.row_names:
                            vecs.append(pred_mat.get(row_names=pred_name))
            elif isinstance(arg, np.ndarray):
                self.logger.warn("linear_analysis.__load_predictions(): " +
                                "instantiating prediction matrix from " +
                                "ndarray, can't verify alignment")
                self.logger.warn("linear_analysis.__load_predictions(): " +
                                 "instantiating prediction matrix from " +
                                 "ndarray, generating generic prediction names")
                pred_names = []
                [pred_names.append("pred_" + str(i + 1))
                 for i in range(self.prediction_arg.shape[0])]

                if self.jco:
                    names = self.jco.col_names
                elif self.parcov:
                    names = self.parcov.col_names
                else:
                    raise Exception("linear_analysis.__load_predictions(): " +
                                    "ndarray passed for predicitons " +
                                    "requires jco or parcov to get " +
                                    "parameter names")
                pred_matrix = Matrix(x=self.prediction_arg,
                                           row_names=pred_names,
                                           col_names=names)
                for pred_name in pred_names:
                    vecs.append(pred_matrix.extract(row_names=pred_name).T)
            else:
                raise Exception("unrecognized predictions argument: " +
                                str(arg))
        if len(row_names) > 0:
            extract = self.jco.extract(row_names=row_names)
            for row_name in row_names:
                vecs.append(extract.get(row_names=row_name).T)

            # call obscov to load __obscov so that __obscov
            # (priavte) can be manipulated
            self.obscov
            self.__obscov.drop(row_names, axis=0)
        self.__predictions = vecs
        self.log("loading forecasts")
        return self.__predictions

    # these property decorators help keep from loading potentially
    # unneeded items until they are called
    # returns a reference - cheap, but can be dangerous


    @property
    def parcov(self):
        if not self.__parcov:
            self.__load_parcov()
        return self.__parcov


    @property
    def obscov(self):
        if not self.__obscov:
            self.__load_obscov()
        return self.__obscov


    @property
    def jco(self):
        if not self.__jco:
            self.__load_jco()
        return self.__jco


    @property
    def predictions(self):
        if not self.__predictions:
            self.__load_predictions()
        return self.__predictions

    @property
    def forecasts(self):
        return self.predictions

    @property
    def pst(self):
        if self.__pst is None and self.pst_arg is None:
            raise Exception("linear_analysis.pst: can't access self.pst:" +
                            "no pest control argument passed")
        elif self.__pst:
            return self.__pst
        else:
            self.__load_pst()


    @property
    def fehalf(self):
        """set the KL parcov scaling matrix attribute
        """
        if self.__fehalf != None:
            return self.__fehalf
        self.log("fehalf")
        self.__fehalf = self.parcov.u * (self.parcov.s ** (0.5))
        self.log("fehalf")
        return self.__fehalf


    @property
    def qhalf(self):
        """set the square root of the cofactor matrix attribute
        """
        if self.__qhalf != None:
            return self.__qhalf
        self.log("qhalf")
        self.__qhalf = self.obscov ** (-0.5)
        self.log("qhalf")
        return self.__qhalf

    @property
    def qhalfx(self):
        """set the half normal matrix attribute
        """
        if self.__qhalfx is None:
            self.log("qhalfx")
            self.__qhalfx = self.qhalf * self.jco
            self.log("qhalfx")
        return self.__qhalfx

    @property
    def xtqx(self):
        """set the normal matrix attribute
        """
        if self.__xtqx is None:
            self.log("xtqx")
            self.__xtqx = self.jco.T * (self.obscov ** -1) * self.jco
            self.log("xtqx")
        return self.__xtqx


    @property
    def prior_parameter(self):
        """the prior parameter covariance matrix
        """
        return self.parcov

    @property
    def prior_forecast(self):
        """thin wrapper for prior_prediction
        """
        return self.prior_prediction

    @property
    def prior_prediction(self):
        """get a dict of prior prediction variances
        Parameters:
        ----------
            None
        Returns:
        -------
            dict{prediction name(str):prior variance(float)}
        """
        if self.__prior_prediction is not None:
            return self.__prior_prediction
        else:
            if self.predictions is not None:
                self.log("propagating prior to predictions")
                pred_dict = {}
                for prediction in self.predictions:
                    var = (prediction.T * self.parcov * prediction).x[0, 0]
                    pred_dict[prediction.col_names[0]] = var
                self.__prior_prediction = pred_dict
                self.log("propagating prior to predictions")
            else:
                self.__prior_prediction = {}
            return self.__prior_prediction


    def apply_karhunen_loeve_scaling(self):
        """apply karhuene-loeve scaling to the jacobian matrix.

            This scaling is not necessary for analyses using Schur's
            complement, but can be very important for error variance
            analyses.  This operation effectively transfers prior knowledge
            specified in the parcov to the jacobian and reset parcov to the
            identity matrix.
        """
        cnames = copy.deepcopy(self.jco.col_names)
        self.__jco *= self.fehalf
        self.__jco.col_names = cnames
        self.__parcov = self.parcov.identity


    def clean(self):
        """drop regularization and prior information observation from the jco
        """
        if self.pst_arg is None:
            self.logger.warn("linear_analysis.clean(): not pst object")
            return
        if not self.pst.estimation and self.pst.nprior > 0:
            self.drop_prior_information()


    def reset_parcov(self,arg=None):
        """reset the parcov attribute to None
        Parameters:
        ----------
            arg (str or matrix) : the value to assign to the parcov_arg attrib
        Returns:
        -------
            None
        """
        self.logger.warn("resetting parcov")
        self.__parcov = None
        if arg is not None:
            self.parcov_arg = arg


    def reset_obscov(self,arg=None):
        """reset the obscov attribute to None
        Parameters:
        ----------
            arg (str or matrix) : the value to assign to the obscov_arg attrib
        Returns:
        -------
            None
        """
        self.logger.warn("resetting obscov")
        self.__obscov = None
        if arg is not None:
            self.obscov_arg = arg


    def drop_prior_information(self):
        """drop the prior information from the jco and pst attributes
        """
        nprior_str = str(self.pst.nprior)
        self.log("removing " + nprior_str + " prior info from jco, pst, and " +
                                            "obs cov")
        #pi_names = list(self.pst.prior_information.pilbl.values)
        pi_names = list(self.pst.prior_names)
        if self.jco is not None:
            self.__jco.drop(pi_names, axis=0)
        self.__pst.prior_information = self.pst.null_prior
        self.__pst.control_data.pestmode = "estimation"
        #self.__obscov.drop(pi_names,axis=0)
        self.log("removing " + nprior_str + " prior info from jco and pst")


    def get(self,par_names=None,obs_names=None,astype=None):
        """method to get a new LinearAnalysis class using a
             subset of parameters and/or observations
         Parameters:
         ----------
            par_names (enumerable of str) : par names for new object
            obs_names (enumerable of str) : obs names for new object
            astype (either schur or errvar type) : type to cast the new object
        Returns:
        -------
            LinearAnalysis object
        """
        # make sure we aren't fooling with unwanted prior information
        self.clean()
        # if there is nothing to do but copy
        if par_names is None and obs_names is None:
            if astype is not None:
                self.logger.warn("LinearAnalysis.get(): astype is not None, " +
                                 "but par_names and obs_names are None so" +
                                 "\n  ->Omitted attributes will not be " +
                                 "propagated to new instance")
            else:
                return copy.deepcopy(self)
        # make sure the args are lists
        if par_names is not None and not isinstance(par_names, list):
            par_names = [par_names]
        if obs_names is not None and not isinstance(obs_names, list):
            obs_names = [obs_names]

        if par_names is None:
            par_names = self.jco.col_names
        if obs_names is None:
            obs_names = self.jco.row_names
        # if possible, get a new parcov
        if self.parcov:
            new_parcov = self.parcov.get(col_names=par_names)
        else:
            new_parcov = None
        # if possible, get a new obscov
        if self.obscov_arg is not None:
            new_obscov = self.obscov.get(row_names=obs_names)
        else:
            new_obscov = None
        # if possible, get a new pst
        if self.pst_arg is not None:
            new_pst = self.pst.get(par_names=par_names,obs_names=obs_names)
        else:
            new_pst = None
        if self.predictions:
            new_preds = []
            for prediction in self.predictions:
                new_preds.append(prediction.get(row_names=par_names))
        else:
            new_preds = None
        if self.jco_arg is not None:
            new_jco = self.jco.get(row_names=obs_names, col_names=par_names)
        else:
            new_jco = None
        if astype is not None:
            return astype(jco=new_jco, pst=new_pst, parcov=new_parcov,
                          obscov=new_obscov, predictions=new_preds,
                          verbose=False)
        else:
            # return a new object of the same type
            return type(self)(jco=new_jco, pst=new_pst, parcov=new_parcov,
                              obscov=new_obscov, predictions=new_preds,
                              verbose=False)


    def adjust_obscov_resfile(self, resfile=None):
        """reset the elements of obscov by scaling the implied weights
        based on the phi components in res_file
        """
        self.pst.adjust_weights_resfile(resfile)
        self.__obscov.from_observation_data(self.pst)