<commit_before>import numpy as np
from sklearn.base import BaseEstimator
from sklearn.utils import check_array, as_float_array
from sklearn.utils.extmath import fast_logdet

import pyquic

def log_likelihood(covariance, precision):
    """Computes ...

    Parameters
    ----------
    covariance : 2D ndarray (n_features, n_features)
        Maximum Likelihood Estimator of covariance

    precision : 2D ndarray (n_features, n_features)
        The precision matrix of the covariance model to be tested

    Returns
    -------
    log-likelihood
    """
    assert covariance.shape == precision.shape
    dim, _ = precision.shape
    log_likelihood_ = np.trace(covariance * precision) - fast_logdet(precision)
    log_likelihood_ -= dim * np.log(2 * np.pi)
    return log_likelihood_

def kl_loss(covariance, precision):
    """Computes the KL divergence between precision estimate and
    reference covariance.

    The loss is computed as:

        Trace(Theta * Sigma) - log(Theta * Sigma) - dim(Sigma)

    Parameters
    ----------
    covariance : 2D ndarray (n_features, n_features)
        Maximum Likelihood Estimator of covariance

    precision : 2D ndarray (n_features, n_features)
        The precision matrix of the covariance model to be tested

    Returns
    -------
    KL-divergence
    """
    assert covariance.shape == precision.shape
    dim, _ = precision.shape
    mul_cov_prec = covariance * precision
    return np.trace(mul_cov_prec) - np.log(mul_cov_prec) - dim

def quadratic_loss(covariance, precision):
    """Computes ...

    Parameters
    ----------
    covariance : 2D ndarray (n_features, n_features)
        Maximum Likelihood Estimator of covariance

    precision : 2D ndarray (n_features, n_features)
        The precision matrix of the covariance model to be tested

    Returns
    -------
    Quadratic loss
    """
    assert covariance.shape == precision.shape
    dim, _ = precision.shape
    return np.trace((covariance * precision - np.eye(dim))**2)

def quic(S, lam, mode='default', tol=1e-6, max_iter=1000,
        Theta0=None, Sigma0=None, path=None, msg=0):
    """Fits the inverse covariance model according to the given training
    data and parameters.

    Parameters
    -----------
    S : 2D ndarray, shape (n_features, n_features)
        Empirical covariance or correlation matrix.

    Other parameters described in `class InverseCovariance`.

    Returns
    -------
    Theta :
    Sigma :
    opt :
    cputime :
    iters :
    dGap :
    """
    assert mode in ['default', 'path', 'trace'],\
            'mode = \'default\', \'path\' or \'trace\'.'

    Sn, Sm = S.shape
    if Sn != Sm:
        raise ValueError("Input data must be square. S shape = {}".format(
                         S.shape))
        return

    # Regularization parameter matrix L.
    if isinstance(lam, float):
        _lam = np.empty((Sn, Sm))
        _lam[:] = lam
    else:
        assert L.shape == S.shape, 'lam, S shape mismatch.'
        _lam = as_float_array(lam, copy=False, force_all_finite=False)

    # Defaults.
    optSize = 1
    iterSize = 1
    if mode is "trace":
        optSize = max_iter

    # Default Theta0, Sigma0 when both are None.
    if Theta0 is None and Sigma0 is None:
        Theta0 = np.eye(Sn)
        Sigma0 = np.eye(Sn)

    assert Theta0 is not None,\
            'Theta0 and Sigma0 must both be None or both specified.'
    assert Sigma0 is not None,\
            'Theta0 and Sigma0 must both be None or both specified.'
    assert Theta0.shape == S.shape, 'Theta0, S shape mismatch.'
    assert Sigma0.shape == S.shape, 'Theta0, Sigma0 shape mismatch.'
    Theta0 = as_float_array(Theta0, copy=False, force_all_finite=False)
    Sigma0 = as_float_array(Sigma0, copy=False, force_all_finite=False)

    if mode is 'path':
        assert path is not None, 'Please specify the path scaling values.'
        path_len = len(path)
        optSize = path_len
        iterSize = path_len

        # Note here: memory layout is important:
        # a row of X/W holds a flattened Sn x Sn matrix,
        # one row for every element in _path_.
        Theta = np.empty((path_len, Sn * Sn))
        Theta[0,:] = Theta0.ravel()
        Sigma = np.empty((path_len, Sn * Sn))
        Sigma[0,:] = Sigma0.ravel()
    else:
        path = np.empty(1)
        path_len = len(path)

        Theta = np.empty(Theta0.shape)
        Theta[:] = Theta0
        Sigma = np.empty(Sigma0.shape)
        Sigma[:] = Sigma0

    # Run QUIC.
    opt = np.zeros(optSize)
    cputime = np.zeros(optSize)
    dGap = np.zeros(optSize)
    iters = np.zeros(iterSize, dtype=np.uint32)
    pyquic.quic(mode, Sn, S, _lam, path_len, path, tol, msg, max_iter,
                Theta, Sigma, opt, cputime, iters, dGap)

    if optSize == 1:
        opt = opt[0]
        cputime = cputime[0]
        dGap = dGap[0]

    if iterSize == 1:
        iters = iters[0]

    return Theta, Sigma, opt, cputime, iters, dGap

class InverseCovariance(BaseEstimator):
    """
    Computes a sparse inverse covariance matrix estimation using quadratic
    approximation.

    The inverse covariance is estimated the sample covariance estimate
    $S$ as an input such that:

    $T_hat = max_{\Theta} logdet(Theta) - Trace(ThetaS) - \lambda|\Theta|_1 $

    Parameters
    -----------
    lam : scalar or 2D ndarray, shape (n_features, n_features) (default=0.5)
        Regularization parameters per element of the inverse covariance matrix.

    mode : one of 'default', 'path', or 'trace'
        Computation mode.

    tol : float (default=1e-6)
        Convergence threshold.

    max_iter : int (default=1000)
        Maximum number of Newton iterations.

    Theta0 : 2D ndarray, shape (n_features, n_features) (default=None)
        Initial guess for the inverse covariance matrix. If not provided, the
        diagonal identity matrix is used.

    Sigma0 : 2D ndarray, shape (n_features, n_features) (default=None)
        Initial guess for the covariance matrix. If not provided the diagonal
        identity matrix is used.

    path : array of floats (default=None)
        In "path" mode, an array of float values for scaling L.

    verbose : int (default=0)
        Verbosity level.

    method : one of 'quic', 'quicanddirty', 'ETC' (default=quic)

    metric : one of 'log_likelihood' (default), 'frobenius', 'spectral', 'kl',
             or 'quadratic'
        Used in self.score().

    initialize_method : one of 'corrcoef', 'cov'

    Attributes
    ----------
    covariance_ : 2D ndarray, shape (n_features, n_features)
        Estimated covariance matrix

    precision_ : 2D ndarray, shape (n_features, n_features)
        Estimated pseudo-inverse matrix.

    opt_ :

    cputime_ :

    iters_ :

    duality_gap_ :

    """
    def __init__(self, lam=0.5, mode='default', tol=1e-6, max_iter=1000,
                 Theta0=None, Sigma0=None, path=None, verbose=0, method='quic'
                 metric='log_likelihood', initialize_method='corrcoef'):
        self.lam = lam
        self.mode = mode
        self.tol = tol
        self.max_iter = max_iter
        self.Theta0 = Theta0
        self.Sigma0 = Sigma0
        self.path = path
        self.verbose = verbose
        self.method = method
        self.metric = metric
        self.initialize_method = initialize_method

        self.covariance_ = None
        self.precision_ = None
        self.opt_ = None
        self.cputime_ = None
        self.iters_ = None
        self.duality_gap_ = None

        super(InverseCovariance, self).__init__()

    def initialize_coefficients(self, X):
        if self.initialize_method == 'corrcoef':
            return np.corrcoef(X), 1.0
        elif self.initialize_method == 'cov':
            init_cov = np.cov(X)
            return init_cov, np.max(np.triu(init_cov)
        else:
            raise ValueError("initialize_method must be 'corrcoeff' or 'cov'."))

    def fit(self, X, y=None, **fit_params):
        """Fits the inverse covariance model according to the given training
        data and parameters.

        Parameters
        -----------
        X : 2D ndarray, shape (n_features, n_features)
            Input data.

        Returns
        -------
        self
        """
        X = check_array(X)
        X = as_float_array(X, copy=False, force_all_finite=False)
        S, scale_lam = self.initialize_coefficients(X)
        if self.method is 'quic':
            (self.precision_, self.covariance_, self.opt_, self.cputime_,
            self.iters_, self.duality_gap_) = quic(S,
                                                self.lam * scale_lam,
                                                mode=self.mode,
                                                tol=self.tol,
                                                max_iter=self.max_iter,
                                                Theta0=self.Theta0,
                                                Sigma0=self.Sigma0,
                                                path=self.path,
                                                msg=self.verbose)
        else:
            raise NotImplementedError(
                "Only method='quic' has been implemented.")

        return self

    def score(self, X_test, y=None):
        """Computes the score between cov/prec of X_test and X via 'metric'.

        ----------
        X_test : array-like, shape = [n_samples, n_features]
            Test data of which we compute the likelihood, where n_samples is
            the number of samples and n_features is the number of features.
            X_test is assumed to be drawn from the same distribution than
            the data used in fit (including centering).

        y : not used.

        Returns
        -------
        res : float
            The likelihood of the data set with `self.covariance_` as an
            estimator of its covariance matrix.
        """
        S, scale_lam = self.initialize_coefficients(X_test)
        if self.method is 'quic':
            precision_test, covariance_test, _, _, _, _ = quic(
                    S,
                    self.lam * scale_lam,
                    mode=self.mode,
                    tol=self.tol,
                    max_iter=self.max_iter,
                    Theta0=self.Theta0,
                    Sigma0=self.Sigma0,
                    path=self.path,
                    msg=self.verbose)
        else:
            raise NotImplementedError(
                "Only method='quic' has been implemented.")

        return self.error_norm(
                precision_test,
                norm=self.metric,
                scaling=True,
                squared=False)

    def error_norm(self, comp_prec, norm='frobenius', scaling=True,
                   squared=True):
        """Computes the error between two inverse-covariance estimators
        (i.e., over precision).

        Parameters
        ----------
        comp_prec : array-like, shape = [n_features, n_features]
            The precision to compare with.

        scaling : bool
            If True (default), the squared error norm is divided by n_features.
            If False, the squared error norm is not rescaled.

        norm : str
            The type of norm used to compute the error between the estimated
            self.precision, self.covariance and the reference `comp_prec`.
            Available error types:

            - 'frobenius' (default): sqrt(tr(A^t.A))
            - 'spectral': sqrt(max(eigenvalues(A^t.A))
            - 'kl': kl-divergence
            - 'quadratic': qudratic loss
            - 'log_likelihood': log likelihood

            The term 'norm' is retained to be compatible with EmpiricalCovariance
            but 'metric' would be more appropriate.

        squared : bool
            Whether to compute the squared error norm or the error norm.
            If True (default), the squared error norm is returned.
            If False, the error norm is returned.

        Returns
        -------
        The error between `self.precision_` and `comp_prec`
        """
        # compute the error
        error = comp_prec - self.precision_

        # compute the error norm
        if norm == "frobenius":
            result = np.sum(error ** 2)
        elif norm == "spectral":
            result = np.amax(linalg.svdvals(np.dot(error.T, error)))
        elif norm == "kl":
            result = kl_loss(self.covariance_, comp_prec)
        elif norm == "quadratic":
            result = quadratic_loss(self.covariance_, comp_prec)
        elif norm == "log_likelihood":
            result = log_likelihood(self.covariance, comp_prec)
        else:
            raise NotImplementedError(
                "Only spectral and frobenius norms are implemented")

        # optionally scale the error norm
        if scaling:
            result = result / error.shape[0]

        # finally get either the squared norm or the norm
        if not squared:
            result = np.sqrt(squared_norm)

        return result

<commit_msg>Fix bugs, old tests pass<commit_after>import numpy as np
from sklearn.base import BaseEstimator
from sklearn.utils import check_array, as_float_array
from sklearn.utils.extmath import fast_logdet

import pyquic

def log_likelihood(covariance, precision):
    """Computes ...

    Parameters
    ----------
    covariance : 2D ndarray (n_features, n_features)
        Maximum Likelihood Estimator of covariance

    precision : 2D ndarray (n_features, n_features)
        The precision matrix of the covariance model to be tested

    Returns
    -------
    log-likelihood
    """
    assert covariance.shape == precision.shape
    dim, _ = precision.shape
    log_likelihood_ = np.trace(covariance * precision) - fast_logdet(precision)
    log_likelihood_ -= dim * np.log(2 * np.pi)
    return log_likelihood_

def kl_loss(covariance, precision):
    """Computes the KL divergence between precision estimate and
    reference covariance.

    The loss is computed as:

        Trace(Theta * Sigma) - log(Theta * Sigma) - dim(Sigma)

    Parameters
    ----------
    covariance : 2D ndarray (n_features, n_features)
        Maximum Likelihood Estimator of covariance

    precision : 2D ndarray (n_features, n_features)
        The precision matrix of the covariance model to be tested

    Returns
    -------
    KL-divergence
    """
    assert covariance.shape == precision.shape
    dim, _ = precision.shape
    mul_cov_prec = covariance * precision
    return np.trace(mul_cov_prec) - np.log(mul_cov_prec) - dim

def quadratic_loss(covariance, precision):
    """Computes ...

    Parameters
    ----------
    covariance : 2D ndarray (n_features, n_features)
        Maximum Likelihood Estimator of covariance

    precision : 2D ndarray (n_features, n_features)
        The precision matrix of the covariance model to be tested

    Returns
    -------
    Quadratic loss
    """
    assert covariance.shape == precision.shape
    dim, _ = precision.shape
    return np.trace((covariance * precision - np.eye(dim))**2)

def quic(S, lam, mode='default', tol=1e-6, max_iter=1000,
        Theta0=None, Sigma0=None, path=None, msg=0):
    """Fits the inverse covariance model according to the given training
    data and parameters.

    Parameters
    -----------
    S : 2D ndarray, shape (n_features, n_features)
        Empirical covariance or correlation matrix.

    Other parameters described in `class InverseCovariance`.

    Returns
    -------
    Theta :
    Sigma :
    opt :
    cputime :
    iters :
    dGap :
    """
    assert mode in ['default', 'path', 'trace'],\
            'mode = \'default\', \'path\' or \'trace\'.'

    Sn, Sm = S.shape
    if Sn != Sm:
        raise ValueError("Input data must be square. S shape = {}".format(
                         S.shape))
        return

    # Regularization parameter matrix L.
    if isinstance(lam, float):
        _lam = np.empty((Sn, Sm))
        _lam[:] = lam
    else:
        assert L.shape == S.shape, 'lam, S shape mismatch.'
        _lam = as_float_array(lam, copy=False, force_all_finite=False)

    # Defaults.
    optSize = 1
    iterSize = 1
    if mode is "trace":
        optSize = max_iter

    # Default Theta0, Sigma0 when both are None.
    if Theta0 is None and Sigma0 is None:
        Theta0 = np.eye(Sn)
        Sigma0 = np.eye(Sn)

    assert Theta0 is not None,\
            'Theta0 and Sigma0 must both be None or both specified.'
    assert Sigma0 is not None,\
            'Theta0 and Sigma0 must both be None or both specified.'
    assert Theta0.shape == S.shape, 'Theta0, S shape mismatch.'
    assert Sigma0.shape == S.shape, 'Theta0, Sigma0 shape mismatch.'
    Theta0 = as_float_array(Theta0, copy=False, force_all_finite=False)
    Sigma0 = as_float_array(Sigma0, copy=False, force_all_finite=False)

    if mode is 'path':
        assert path is not None, 'Please specify the path scaling values.'
        path_len = len(path)
        optSize = path_len
        iterSize = path_len

        # Note here: memory layout is important:
        # a row of X/W holds a flattened Sn x Sn matrix,
        # one row for every element in _path_.
        Theta = np.empty((path_len, Sn * Sn))
        Theta[0,:] = Theta0.ravel()
        Sigma = np.empty((path_len, Sn * Sn))
        Sigma[0,:] = Sigma0.ravel()
    else:
        path = np.empty(1)
        path_len = len(path)

        Theta = np.empty(Theta0.shape)
        Theta[:] = Theta0
        Sigma = np.empty(Sigma0.shape)
        Sigma[:] = Sigma0

    # Run QUIC.
    opt = np.zeros(optSize)
    cputime = np.zeros(optSize)
    dGap = np.zeros(optSize)
    iters = np.zeros(iterSize, dtype=np.uint32)
    pyquic.quic(mode, Sn, S, _lam, path_len, path, tol, msg, max_iter,
                Theta, Sigma, opt, cputime, iters, dGap)

    if optSize == 1:
        opt = opt[0]
        cputime = cputime[0]
        dGap = dGap[0]

    if iterSize == 1:
        iters = iters[0]

    return Theta, Sigma, opt, cputime, iters, dGap

class InverseCovariance(BaseEstimator):
    """
    Computes a sparse inverse covariance matrix estimation using quadratic
    approximation.

    The inverse covariance is estimated the sample covariance estimate
    $S$ as an input such that:

    $T_hat = max_{\Theta} logdet(Theta) - Trace(ThetaS) - \lambda|\Theta|_1 $

    Parameters
    -----------
    lam : scalar or 2D ndarray, shape (n_features, n_features) (default=0.5)
        Regularization parameters per element of the inverse covariance matrix.

    mode : one of 'default', 'path', or 'trace'
        Computation mode.

    tol : float (default=1e-6)
        Convergence threshold.

    max_iter : int (default=1000)
        Maximum number of Newton iterations.

    Theta0 : 2D ndarray, shape (n_features, n_features) (default=None)
        Initial guess for the inverse covariance matrix. If not provided, the
        diagonal identity matrix is used.

    Sigma0 : 2D ndarray, shape (n_features, n_features) (default=None)
        Initial guess for the covariance matrix. If not provided the diagonal
        identity matrix is used.

    path : array of floats (default=None)
        In "path" mode, an array of float values for scaling L.

    verbose : int (default=0)
        Verbosity level.

    method : one of 'quic', 'quicanddirty', 'ETC' (default=quic)

    metric : one of 'log_likelihood' (default), 'frobenius', 'spectral', 'kl',
             or 'quadratic'
        Used in self.score().

    initialize_method : one of 'corrcoef', 'cov'

    Attributes
    ----------
    covariance_ : 2D ndarray, shape (n_features, n_features)
        Estimated covariance matrix

    precision_ : 2D ndarray, shape (n_features, n_features)
        Estimated pseudo-inverse matrix.

    opt_ :

    cputime_ :

    iters_ :

    duality_gap_ :

    """
    def __init__(self, lam=0.5, mode='default', tol=1e-6, max_iter=1000,
                 Theta0=None, Sigma0=None, path=None, verbose=0, method='quic',
                 metric='log_likelihood', initialize_method='corrcoef'):
        self.lam = lam
        self.mode = mode
        self.tol = tol
        self.max_iter = max_iter
        self.Theta0 = Theta0
        self.Sigma0 = Sigma0
        self.path = path
        self.verbose = verbose
        self.method = method
        self.metric = metric
        self.initialize_method = initialize_method

        self.covariance_ = None
        self.precision_ = None
        self.opt_ = None
        self.cputime_ = None
        self.iters_ = None
        self.duality_gap_ = None

        super(InverseCovariance, self).__init__()

    def initialize_coefficients(self, X):
        if self.initialize_method is 'corrcoef':
            return np.corrcoef(X), 1.0
        elif self.initialize_method is 'cov':
            init_cov = np.cov(X)
            return init_cov, np.max(np.triu(init_cov))
        else:
            raise ValueError("initialize_method must be 'corrcoeff' or 'cov'.")

    def fit(self, X, y=None, **fit_params):
        """Fits the inverse covariance model according to the given training
        data and parameters.

        Parameters
        -----------
        X : 2D ndarray, shape (n_features, n_features)
            Input data.

        Returns
        -------
        self
        """
        X = check_array(X)
        X = as_float_array(X, copy=False, force_all_finite=False)
        S, scale_lam = self.initialize_coefficients(X)
        if self.method is 'quic':
            (self.precision_, self.covariance_, self.opt_, self.cputime_,
            self.iters_, self.duality_gap_) = quic(S,
                                                self.lam * scale_lam,
                                                mode=self.mode,
                                                tol=self.tol,
                                                max_iter=self.max_iter,
                                                Theta0=self.Theta0,
                                                Sigma0=self.Sigma0,
                                                path=self.path,
                                                msg=self.verbose)
        else:
            raise NotImplementedError(
                "Only method='quic' has been implemented.")

        return self

    def score(self, X_test, y=None):
        """Computes the score between cov/prec of X_test and X via 'metric'.

        ----------
        X_test : array-like, shape = [n_samples, n_features]
            Test data of which we compute the likelihood, where n_samples is
            the number of samples and n_features is the number of features.
            X_test is assumed to be drawn from the same distribution than
            the data used in fit (including centering).

        y : not used.

        Returns
        -------
        res : float
            The likelihood of the data set with `self.covariance_` as an
            estimator of its covariance matrix.
        """
        S, scale_lam = self.initialize_coefficients(X_test)
        if self.method is 'quic':
            precision_test, covariance_test, _, _, _, _ = quic(
                    S,
                    self.lam * scale_lam,
                    mode=self.mode,
                    tol=self.tol,
                    max_iter=self.max_iter,
                    Theta0=self.Theta0,
                    Sigma0=self.Sigma0,
                    path=self.path,
                    msg=self.verbose)
        else:
            raise NotImplementedError(
                "Only method='quic' has been implemented.")

        return self.error_norm(
                precision_test,
                norm=self.metric,
                scaling=True,
                squared=False)

    def error_norm(self, comp_prec, norm='frobenius', scaling=True,
                   squared=True):
        """Computes the error between two inverse-covariance estimators
        (i.e., over precision).

        Parameters
        ----------
        comp_prec : array-like, shape = [n_features, n_features]
            The precision to compare with.

        scaling : bool
            If True (default), the squared error norm is divided by n_features.
            If False, the squared error norm is not rescaled.

        norm : str
            The type of norm used to compute the error between the estimated
            self.precision, self.covariance and the reference `comp_prec`.
            Available error types:

            - 'frobenius' (default): sqrt(tr(A^t.A))
            - 'spectral': sqrt(max(eigenvalues(A^t.A))
            - 'kl': kl-divergence
            - 'quadratic': qudratic loss
            - 'log_likelihood': log likelihood

            The term 'norm' is retained to be compatible with EmpiricalCovariance
            but 'metric' would be more appropriate.

        squared : bool
            Whether to compute the squared error norm or the error norm.
            If True (default), the squared error norm is returned.
            If False, the error norm is returned.

        Returns
        -------
        The error between `self.precision_` and `comp_prec`
        """
        # compute the error
        error = comp_prec - self.precision_

        # compute the error norm
        if norm == "frobenius":
            result = np.sum(error ** 2)
        elif norm == "spectral":
            result = np.amax(linalg.svdvals(np.dot(error.T, error)))
        elif norm == "kl":
            result = kl_loss(self.covariance_, comp_prec)
        elif norm == "quadratic":
            result = quadratic_loss(self.covariance_, comp_prec)
        elif norm == "log_likelihood":
            result = log_likelihood(self.covariance, comp_prec)
        else:
            raise NotImplementedError(
                "Only spectral and frobenius norms are implemented")

        # optionally scale the error norm
        if scaling:
            result = result / error.shape[0]

        # finally get either the squared norm or the norm
        if not squared:
            result = np.sqrt(squared_norm)

        return result

<|endoftext|>


<commit_before># encoding: utf-8
# Generic fabfile.py
#
# See README for instructions on how to use fabmanager.

import os
import datetime
import cStringIO

from fabric.api import *
from fabric.contrib import django
from fabric.contrib import files
from fabric.contrib import console

from django.conf import settings as django_settings

# Paths related to fabmanager
fabmanager_dir = os.path.dirname(os.path.abspath(__file__))
templates_dir  = os.path.join(fabmanager_dir, 'templates/fabmanager/')

# Environments - to be extended with ENVS.update()
ENVS = {}

# Python
GET_PYTHON_VERSION = "python -V 2>&1 | cut -f2 -d' ' | cut -f-2 -d."

# virtualenvwrapper
VIRTUALENVWRAPPER_SCRIPT = '/usr/local/bin/virtualenvwrapper.sh'
VIRTUALENVWRAPPER_PREFIX = 'export WORKON_HOME=%(workon)s && source %(script)s'

# Django strings should be interpolated by ENVS[project]
VIRTUALENV_DIR     = '%(workon)s/%(virtualenv)s'
SITE_PACKAGES_DIR  = 'lib/python%s/site-packages'
DJANGO_PROJECT_DIR = "%(workon)s/%(virtualenv)s/%(project)s"
DJANGO_PREFIX      = "export PYTHONPATH=%(workon)s/%(virtualenv)s:%(workon)s/%(virtualenv)s/%(project)s " \
                     "DJANGO_SETTINGS_MODULE=%(project)s.%(settings)s && " \
                     "source %(workon)s/%(virtualenv)s/bin/activate"

# Apache
CONFIG_DIR         = '%(workon)s/%(virtualenv)s/%(project)s/config'
MEDIA_DIR          = '%(workon)s/%(virtualenv)s/%(project)s/media'
STATIC_DIR         = '%(workon)s/%(virtualenv)s/%(project)s/static'
FAVICON_DIR        = '%(workon)s/%(virtualenv)s/%(project)s/static/img'
APACHE_CONF        = CONFIG_DIR+'/apache_%(virtualenv)s.conf'
WSGI_CONF          = CONFIG_DIR+'/wsgi_%(virtualenv)s.py'

# MySQL
MYSQL_PREFIX       = 'mysql -u root -p -e %s'
PIP_INSTALL_PREFIX = 'pip install -r config/required-packages.pip'

# Aliases for common tasks at server
ALIASES = dict(
    gs='git status',
    gd='git diff',
    gl='git pull',
    gp='git push',
    gc='git commit -v -a',
    gb='git branch',
    gk='git checkout',
    gm='git checkout master',
    glog='git log --oneline --decorate',
    glogg='git log --oneline --decorate --graph',
    mng='django-admin.py',
)

# Setup commands

def _setup_environment(environment):
    """
    Sets up env variables for this session
    """
    env.environment = environment
    env.project = ENVS[environment]
    env.hosts = [env.project['host']]

def _require_environment():
    """Checks if env.environment and env.host exist"""
    require('environment', 'host', provided_by=ENVS.keys())

def _interpolate(string):
    """Interpolates string with dictionary provided by ENVS"""
    return string % env.project

def _virtualenvwrapper_prefix():
    """Prefix to be able to invoke virtualenvwrapper commands"""
    return VIRTUALENVWRAPPER_PREFIX % {
        'workon': env.project['workon'],
        'script': VIRTUALENVWRAPPER_SCRIPT,
    }

def _python_version():
    """Checks python version on remote virtualenv"""
    with settings(hide('commands', 'warnings'), warn_only=True):
        with prefix(_django_prefix()):
            result = run(GET_PYTHON_VERSION)
            if result.failed:
                abort('Could not determine Python version at virtualenv %s' % env.environment)
    return result

def _django_prefix():
    """Prefix to wrap commands with necessary virtualenv and variables"""
    return _interpolate(DJANGO_PREFIX)

def _django_project_dir():
    """Path to current project' directory"""
    return _interpolate(DJANGO_PROJECT_DIR)

def _parse_alias(command):
    """
    Mimics a bash alias: if command starts with a word that is present in ALIASES
    dictionary, substitutes that word for the value found, and maintains te rest.
    """
    words = command.split(' ')
    if not words or words[0] not in ALIASES.keys():
        return command
    else:
        return '%s %s' % (ALIASES[words[0]], ' '.join(words[1:]))

# Remote commands

def remote(command):
    """
    Issues a generic command at project's directory level
    """
    _require_environment()
    with prefix(_django_prefix()):
        with cd(_django_project_dir()):
            run(_parse_alias(command))

def update():
    """
    Updates server from git pull
    """
    branch = env.project.get('git_branch', 'master')
    with settings(hide('warnings'), warn_only=True):
        remote('git pull origin %s && django-admin.py syncdb && django-admin.py migrate && touch config/wsgi*' % branch)

def status():
    """
    Checks git log and status
    """
    remote('glogg -n 20 && echo "" && git status')

def python():
    """Tries to figure out Python version on server side"""
    _require_environment()
    print 'Python version on virtualenv %s: %s' % (env.environment, _python_version())

def backup():
    """
    Backup server's database and copy tar.gz to local ../backup dir
    TODO: create a restore task
    """
    _require_environment()

    # Uses local Django settings to extract username/password to access remote database
    django.settings_module(env.project['settings'])
    database = django_settings.DATABASES['default']

    # Remote side
    with prefix(_django_prefix()):
        with cd(_django_project_dir()):
            # Creates dir to store backup, avoiding existing similar names
            dirname = '../backup/%s_%s' % (datetime.date.today().strftime('%Y%m%d'), env.environment)
            index = 0
            while files.exists(dirname) or files.exists('%s.tar.gz' % dirname):
                index += 1
                dirname += '.%s' % index
            run('mkdir -p %s' % dirname)

            # Backup
            run('mysqldump -u %s -p%s %s > %s/%s.sql' % (
                database['USER'],
                database['PASSWORD'],
                database['NAME'],
                dirname,
                database['NAME'],
            ))
            run('django-admin.py dumpdata auth > %s/auth.json' % dirname)
            run('cp -R media/uploads %s/' % dirname)
            run('cp -R media/newsletter %s/' % dirname)
            with hide('stdout'):
                run('tar -czvf %s.tar.gz %s/' % (dirname, dirname))
            run('rm -rf %s/' % dirname)

            # Download backup?
            if console.confirm('Download backup?'):
                get('%s.tar.gz' % dirname, '../backup')

def _generate_conf(conf_file, variables):
    """Generates conf file from template, and optionally saves it"""
    # File names
    filename_parts = conf_file.split('.')
    filename_parts.insert(1, env.environment)
    input_file = os.path.join(templates_dir, conf_file)
    output_file = 'config/%s_%s.%s' % tuple(filename_parts)

    # Generate conf file from template
    conf = ''
    output = cStringIO.StringIO()
    try:
        with open(input_file, 'r') as input:
            for line in input:
                output.write(line % variables)
        conf = output.getvalue()
    finally:
        output.close()

    # Shows conf file and optionally saves it
    print conf
    if console.confirm('Save to %s?' % output_file, default=False):
        with open(output_file, 'w') as output:
            output.write(conf)

def apache():
    """Generates Apache conf file. Requires: path to WSGI conf file."""
    _require_environment()

    # Dictionary for interpolating template
    config_dir        = _interpolate(CONFIG_DIR)
    host_aliases      = env.project.get('host_aliases', '')
    if host_aliases:
        host_aliases  = 'ServerAlias %s' % host_aliases

    variables = {
        'host':             env.project['host'],
        'host_aliases':     host_aliases,
        'static_admin_dir': '%s/%s/django/contrib/admin/media' % (_interpolate(VIRTUALENV_DIR), SITE_PACKAGES_DIR % _python_version()),
        'media_dir':        _interpolate(MEDIA_DIR),
        'static_dir':       _interpolate(STATIC_DIR),
        'favicon_dir':      _interpolate(FAVICON_DIR),
        'config_dir':       config_dir,
        'wsgi_file':        '%s/wsgi_%s.py' % (config_dir, env.environment),
    }
    _generate_conf('apache.conf', variables)

def wsgi():
    """Generates WSGI conf file"""
    _require_environment()

    # Dictionary for interpolating template
    variables = {
        'project': env.project['project'],
        'settings': env.project['settings'],
        'site_packages': SITE_PACKAGES_DIR % _python_version(),
        }
    _generate_conf('wsgi.py', variables)

def setup():
    """Sets up a new environment"""
    _require_environment()

    # Checks if needed conf files for this environment already exist
    if not os.path.exists('settings_%s.py' % env.environment):
        abort('There is no settings.py for %s - create one, and commit' % env.environment)
    if not os.path.exists('config/apache_%s.conf' % env.environment):
        abort('There is no Apache conf for %s - use task "apache" to generate one, and commit' % env.environment)
    if not os.path.exists('config/wsgi_%s.py' % env.environment):
        abort('There is no WSGI conf for %s - use task "wsgi" to generate one, and commit' % env.environment)

    # Configures virtualenv and clones git repo
    _setup_virtualenv()
    _clone_gitrepo()

    # Issues extra commands at project's level, if any
    extra_commands = env.project.get('extra_commands', [])
    with settings(hide('warnings'), warn_only=True):
        for command in extra_commands:
            remote(command)

    # Sets up Apache, MySQL
    _setup_apache()
    _setup_mysql()

    # Finish installation
    remote(PIP_INSTALL_PREFIX)
    # TODO: create more setup tasks:
    #   - pip install
    #   - syncdb, migrate
    #   - collectstatic

def _setup_virtualenv():
    """Creates virtualenv for environment"""
    if files.exists(_interpolate(VIRTUALENV_DIR)):
        print 'virtualenv %s already exists' % env.environment
    else:
        with prefix(_virtualenvwrapper_prefix()):
            run(_interpolate('mkvirtualenv --no-site-packages %(virtualenv)s'))
            with hide('commands'):
                print 'virtualenv %s created with python %s\n' % (env.environment, run(GET_PYTHON_VERSION))

def _clone_gitrepo():
    """Clones project git repo into virtualenv"""
    branch = env.project.get('git_branch', 'master')
    if files.exists(_interpolate(DJANGO_PROJECT_DIR)):
        print _interpolate('project %(project)s already exists, updating')
        remote('git pull origin %s' % branch)
    else:
        with cd(_interpolate(VIRTUALENV_DIR)):
            run(_interpolate('git clone %(git_repo)s %(project)s'))
            if branch != 'master':
                remote('git fetch origin %s:%s' % (branch, branch))
                remote('git checkout %s' % branch)

def _setup_apache():
    """Configures Apache"""
    if files.exists(_interpolate('/etc/apache2/sites-enabled/%(virtualenv)s')):
        print 'Apache conf for %s already exists' % env.environment
    else:
        sudo(_interpolate('ln -s %s /etc/apache2/sites-enabled/%%(virtualenv)s' % APACHE_CONF))
        sudo('apache2ctl restart')

def _setup_mysql():
    """Creates MySQL database according to env's settings.py"""
    # Uses local Django settings to extract username/password to access remote database
    django.settings_module(env.project['settings'])
    database = django_settings.DATABASES['default']

    # Create database & user, if not already there
    with settings(hide('warnings'), warn_only=True):
        result = run(MYSQL_PREFIX % "\"CREATE DATABASE %(NAME)s DEFAULT CHARACTER SET utf8;\"" % database)
        if result.succeeded:
            run(MYSQL_PREFIX % "\"GRANT ALL ON %(NAME)s.* TO '%(USER)s'@'localhost' IDENTIFIED BY '%(PASSWORD)s';\"" % database)
<commit_msg>Improving setup() and finishing update()<commit_after># encoding: utf-8
# Generic fabfile.py
#
# See README for instructions on how to use fabmanager.

import os
import datetime
import cStringIO

from fabric.api import *
from fabric.contrib import django
from fabric.contrib import files
from fabric.contrib import console

from django.conf import settings as django_settings

# Paths related to fabmanager
fabmanager_dir = os.path.dirname(os.path.abspath(__file__))
templates_dir  = os.path.join(fabmanager_dir, 'templates/fabmanager/')

# Environments - to be extended with ENVS.update()
ENVS = {}

# Python
GET_PYTHON_VERSION = "python -V 2>&1 | cut -f2 -d' ' | cut -f-2 -d."

# virtualenvwrapper
VIRTUALENVWRAPPER_SCRIPT = '/usr/local/bin/virtualenvwrapper.sh'
VIRTUALENVWRAPPER_PREFIX = 'export WORKON_HOME=%(workon)s && source %(script)s'

# Django strings should be interpolated by ENVS[project]
VIRTUALENV_DIR     = '%(workon)s/%(virtualenv)s'
SITE_PACKAGES_DIR  = 'lib/python%s/site-packages'
DJANGO_PROJECT_DIR = "%(workon)s/%(virtualenv)s/%(project)s"
DJANGO_PREFIX      = "export PYTHONPATH=%(workon)s/%(virtualenv)s:%(workon)s/%(virtualenv)s/%(project)s " \
                     "DJANGO_SETTINGS_MODULE=%(project)s.%(settings)s && " \
                     "source %(workon)s/%(virtualenv)s/bin/activate"

# Apache
CONFIG_DIR         = '%(workon)s/%(virtualenv)s/%(project)s/config'
MEDIA_DIR          = '%(workon)s/%(virtualenv)s/%(project)s/media'
STATIC_DIR         = '%(workon)s/%(virtualenv)s/%(project)s/static'
FAVICON_DIR        = '%(workon)s/%(virtualenv)s/%(project)s/static/img'
APACHE_CONF        = CONFIG_DIR+'/apache_%(virtualenv)s.conf'
WSGI_CONF          = CONFIG_DIR+'/wsgi_%(virtualenv)s.py'

# MySQL
MYSQL_PREFIX       = 'mysql -u root -p -e %s'
PIP_INSTALL_PREFIX = 'pip install -r config/required-packages.pip'

# Aliases for common tasks at server
ALIASES = dict(
    gs='git status',
    gd='git diff',
    gl='git pull',
    gp='git push',
    gc='git commit -v -a',
    gb='git branch',
    gk='git checkout',
    gm='git checkout master',
    glog='git log --oneline --decorate',
    glogg='git log --oneline --decorate --graph',
    mng='django-admin.py',
)

# Setup commands

def _setup_environment(environment):
    """
    Sets up env variables for this session
    """
    env.environment = environment
    env.project = ENVS[environment]
    env.hosts = [env.project['host']]

def _require_environment():
    """Checks if env.environment and env.host exist"""
    require('environment', 'host', provided_by=ENVS.keys())

def _interpolate(string):
    """Interpolates string with dictionary provided by ENVS"""
    return string % env.project

def _virtualenvwrapper_prefix():
    """Prefix to be able to invoke virtualenvwrapper commands"""
    return VIRTUALENVWRAPPER_PREFIX % {
        'workon': env.project['workon'],
        'script': VIRTUALENVWRAPPER_SCRIPT,
    }

def _python_version():
    """Checks python version on remote virtualenv"""
    with settings(hide('commands', 'warnings'), warn_only=True):
        with prefix(_django_prefix()):
            result = run(GET_PYTHON_VERSION)
            if result.failed:
                abort('Could not determine Python version at virtualenv %s' % env.environment)
    return result

def _django_prefix():
    """Prefix to wrap commands with necessary virtualenv and variables"""
    return _interpolate(DJANGO_PREFIX)

def _django_project_dir():
    """Path to current project' directory"""
    return _interpolate(DJANGO_PROJECT_DIR)

def _parse_alias(command):
    """
    Mimics a bash alias: if command starts with a word that is present in ALIASES
    dictionary, substitutes that word for the value found, and maintains te rest.
    """
    words = command.split(' ')
    if not words or words[0] not in ALIASES.keys():
        return command
    else:
        return '%s %s' % (ALIASES[words[0]], ' '.join(words[1:]))

# Remote commands

def remote(command):
    """
    Issues a generic command at project's directory level
    """
    _require_environment()
    with prefix(_django_prefix()):
        with cd(_django_project_dir()):
            run(_parse_alias(command))

def update():
    """
    Updates server from git pull
    """
    branch = env.project.get('git_branch', 'master')
    with settings(hide('warnings'), warn_only=True):
        remote(
            'git pull origin %s && '
            'django-admin.py syncdb --noinput && '
            'django-admin.py migrate && '
            'django-admin.py collectstatic --noinput && '
            'touch config/wsgi*'
        % branch)

def status():
    """
    Checks git log and status
    """
    remote('glogg -n 20 && echo "" && git status')

def python():
    """Tries to figure out Python version on server side"""
    _require_environment()
    print 'Python version on virtualenv %s: %s' % (env.environment, _python_version())

def backup():
    """
    Backup server's database and copy tar.gz to local ../backup dir
    TODO: create a restore task
    """
    _require_environment()

    # Uses local Django settings to extract username/password to access remote database
    django.settings_module(env.project['settings'])
    database = django_settings.DATABASES['default']

    # Remote side
    with prefix(_django_prefix()):
        with cd(_django_project_dir()):
            # Creates dir to store backup, avoiding existing similar names
            dirname = '../backup/%s_%s' % (datetime.date.today().strftime('%Y%m%d'), env.environment)
            index = 0
            while files.exists(dirname) or files.exists('%s.tar.gz' % dirname):
                index += 1
                dirname += '.%s' % index
            run('mkdir -p %s' % dirname)

            # Backup
            run('mysqldump -u %s -p%s %s > %s/%s.sql' % (
                database['USER'],
                database['PASSWORD'],
                database['NAME'],
                dirname,
                database['NAME'],
            ))
            run('django-admin.py dumpdata auth > %s/auth.json' % dirname)
            run('cp -R media/uploads %s/' % dirname)
            run('cp -R media/newsletter %s/' % dirname)
            with hide('stdout'):
                run('tar -czvf %s.tar.gz %s/' % (dirname, dirname))
            run('rm -rf %s/' % dirname)

            # Download backup?
            if console.confirm('Download backup?'):
                get('%s.tar.gz' % dirname, '../backup')

def _generate_conf(conf_file, variables):
    """Generates conf file from template, and optionally saves it"""
    # File names
    filename_parts = conf_file.split('.')
    filename_parts.insert(1, env.environment)
    input_file = os.path.join(templates_dir, conf_file)
    output_file = 'config/%s_%s.%s' % tuple(filename_parts)

    # Generate conf file from template
    conf = ''
    output = cStringIO.StringIO()
    try:
        with open(input_file, 'r') as input:
            for line in input:
                output.write(line % variables)
        conf = output.getvalue()
    finally:
        output.close()

    # Shows conf file and optionally saves it
    print conf
    if console.confirm('Save to %s?' % output_file, default=False):
        with open(output_file, 'w') as output:
            output.write(conf)

def apache():
    """Generates Apache conf file. Requires: path to WSGI conf file."""
    _require_environment()

    # Dictionary for interpolating template
    config_dir        = _interpolate(CONFIG_DIR)
    host_aliases      = env.project.get('host_aliases', '')
    if host_aliases:
        host_aliases  = 'ServerAlias %s' % host_aliases

    variables = {
        'host':             env.project['host'],
        'host_aliases':     host_aliases,
        'static_admin_dir': '%s/%s/django/contrib/admin/media' % (_interpolate(VIRTUALENV_DIR), SITE_PACKAGES_DIR % _python_version()),
        'media_dir':        _interpolate(MEDIA_DIR),
        'static_dir':       _interpolate(STATIC_DIR),
        'favicon_dir':      _interpolate(FAVICON_DIR),
        'config_dir':       config_dir,
        'wsgi_file':        '%s/wsgi_%s.py' % (config_dir, env.environment),
    }
    _generate_conf('apache.conf', variables)

def wsgi():
    """Generates WSGI conf file"""
    _require_environment()

    # Dictionary for interpolating template
    variables = {
        'project': env.project['project'],
        'settings': env.project['settings'],
        'site_packages': SITE_PACKAGES_DIR % _python_version(),
        }
    _generate_conf('wsgi.py', variables)

def setup():
    """Sets up a new environment"""
    _require_environment()

    # Checks if needed conf files for this environment already exist
    if not os.path.exists('settings_%s.py' % env.environment):
        abort('There is no settings.py for %s - create one, and commit' % env.environment)
    if not os.path.exists('config/apache_%s.conf' % env.environment):
        abort('There is no Apache conf for %s - use task "apache" to generate one, and commit' % env.environment)
    if not os.path.exists('config/wsgi_%s.py' % env.environment):
        abort('There is no WSGI conf for %s - use task "wsgi" to generate one, and commit' % env.environment)

    # Configures virtualenv and clones git repo
    _setup_virtualenv()
    _clone_gitrepo()

    # Issues extra commands at project's level, if any
    extra_commands = env.project.get('extra_commands', [])
    with settings(hide('warnings'), warn_only=True):
        for command in extra_commands:
            remote(command)

    # Sets up Apache, MySQL
    _setup_apache()
    _setup_mysql()

    # Finish installation
    remote(PIP_INSTALL_PREFIX)
    update()

def _setup_virtualenv():
    """Creates virtualenv for environment"""
    if files.exists(_interpolate(VIRTUALENV_DIR)):
        print 'virtualenv %s already exists' % env.environment
    else:
        with prefix(_virtualenvwrapper_prefix()):
            run(_interpolate('mkvirtualenv --no-site-packages %(virtualenv)s'))
            with hide('commands'):
                print 'virtualenv %s created with python %s\n' % (env.environment, run(GET_PYTHON_VERSION))

def _clone_gitrepo():
    """Clones project git repo into virtualenv"""
    branch = env.project.get('git_branch', 'master')
    if files.exists(_interpolate(DJANGO_PROJECT_DIR)):
        print _interpolate('project %(project)s already exists, updating')
        remote('git pull origin %s' % branch)
    else:
        with cd(_interpolate(VIRTUALENV_DIR)):
            run(_interpolate('git clone %(git_repo)s %(project)s'))
            if branch != 'master':
                remote('git fetch origin %s:%s' % (branch, branch))
                remote('git checkout %s' % branch)

def _setup_apache():
    """Configures Apache"""
    if files.exists(_interpolate('/etc/apache2/sites-enabled/%(virtualenv)s')):
        print 'Apache conf for %s already exists' % env.environment
    else:
        sudo(_interpolate('ln -s %s /etc/apache2/sites-enabled/%%(virtualenv)s' % APACHE_CONF))
        sudo('apache2ctl restart')

def _setup_mysql():
    """Creates MySQL database according to env's settings.py"""
    # Uses local Django settings to extract username/password to access remote database
    django.settings_module(env.project['settings'])
    database = django_settings.DATABASES['default']

    # Create database & user, if not already there
    with settings(hide('warnings'), warn_only=True):
        result = run(MYSQL_PREFIX % "\"CREATE DATABASE %(NAME)s DEFAULT CHARACTER SET utf8;\"" % database)
        if result.succeeded:
            run(MYSQL_PREFIX % "\"GRANT ALL ON %(NAME)s.* TO '%(USER)s'@'localhost' IDENTIFIED BY '%(PASSWORD)s';\"" % database)
<|endoftext|>


<commit_before>import numpy as np
from sklearn.base import BaseEstimator

from . import metrics

def _init_coefs(X, method='corrcoef'):
    if method == 'corrcoef':
        return np.corrcoef(X, rowvar=False), 1.0
    elif method == 'cov':
        init_cov = np.cov(X, rowvar=False)
        return init_cov, np.max(np.abs(np.triu(init_cov)))
    else:
        raise ValueError(("initialize_method must be 'corrcoef' or 'cov', "
            "passed \'{}\' .".format(method)))

def _compute_error(comp_cov, covariance_, precision_, score_metric='frobenius'):
    """Computes the covariance error vs. comp_cov.

    Parameters
    ----------
    comp_cov : array-like, shape = (n_features, n_features)
        The precision to compare with.
        This should normally be the test sample covariance/precision.

    scaling : bool
        If True, the squared error norm is divided by n_features.
        If False (default), the squared error norm is not rescaled.

    score_metric : str
        The type of norm used to compute the error between the estimated
        self.precision, self.covariance and the reference `comp_cov`.
        Available error types:

        - 'frobenius' (default): sqrt(tr(A^t.A))
        - 'spectral': sqrt(max(eigenvalues(A^t.A))
        - 'kl': kl-divergence
        - 'quadratic': quadratic loss
        - 'log_likelihood': negative log likelihood

    squared : bool
        Whether to compute the squared error norm or the error norm.
        If True (default), the squared error norm is returned.
        If False, the error norm is returned.
    """
    if score_metric == "frobenius":
        error = comp_cov - covariance_
        return np.sum(error ** 2)
    elif score_metric == "spectral":
        error = comp_cov - covariance_
        return np.amax(np.linalg.svdvals(np.dot(error.T, error)))
    elif score_metric == "kl":
        return metrics.kl_loss(comp_cov, precision_)
    elif score_metric == "quadratic":
        return metrics.quadratic_loss(comp_cov, precision_)
    elif score_metric == "log_likelihood":
        return -metrics.log_likelihood(comp_cov, precision_)
    else:
        raise NotImplementedError(("Must be frobenius, spectral, kl, "
                                   "quadratic, or log_likelihood"))

def _validate_path(path):
    """Sorts path values from largest to smallest.

    Will warn if path parameter was not already sorted.
    """
    if path is None:
        return None

    new_path = np.array(sorted(set(path), reverse=True))
    if new_path[0] != path[0]:
        print 'Warning: Path must be sorted largest to smallest.'

    return new_path

class InverseCovarianceEstimator(BaseEstimator):
    """
    Base class for inverse covariance estimators.

    Provides initialization method, metrics, scoring function,
    and ebic model selection.

    Parameters
    -----------
    score_metric : one of 'log_likelihood' (default), 'frobenius', 'spectral',
                  'kl', or 'quadratic'
        Used for computing self.score().

    init_method : one of 'corrcoef', 'cov'
        Computes initial covariance and scales lambda appropriately.

    auto_scale : bool
        If True, will compute self.lam_scale_ = max off-diagonal value when
        init_method='cov'.
        If false, then self.lam_scale_ = 1.
        lam_scale_ is used to scale user-supplied self.lam during fit.

    Attributes
    ----------
    covariance_ : 2D ndarray, shape (n_features, n_features)
        Estimated covariance matrix

        This can also be a len(path) list of
        2D ndarray, shape (n_features, n_features)
        (e.g., see mode='path' in QuicGraphLasso)

    precision_ : 2D ndarray, shape (n_features, n_features)
        Estimated pseudo-inverse matrix.

        This can also be a len(path) list of
        2D ndarray, shape (n_features, n_features)
        (e.g., see mode='path' in QuicGraphLasso)

    sample_covariance_ : 2D ndarray, shape (n_features, n_features)
        Estimated sample covariance matrix

    lam_scale_ : (float)
        Additional scaling factor on lambda (due to magnitude of
        sample_covariance_ values).
    """
    def __init__(self, score_metric='log_likelihood', init_method='cov',
                 auto_scale=True):
        self.score_metric = score_metric
        self.init_method = init_method
        self.auto_scale = auto_scale

        self.covariance_ = None  # assumes a matrix of a list of matrices
        self.precision_ = None  # assumes a matrix of a list of matrices

        # these must be updated upon self.fit()
        # the first 4 will be set if self.init_coefs is used.
        self.sample_covariance_ = None
        self.lam_scale_ = None
        self.n_samples = None
        self.n_features = None
        self.path_ = None
        self.is_fitted = False

        super(InverseCovarianceEstimator, self).__init__()

    def init_coefs(self, X):
        """Computes ...

        Initialize the following values:
            self.n_samples
            self.n_features
            self.sample_covariance_
            self.lam_scale_
        """
        self.n_samples, self.n_features = X.shape
        self.sample_covariance_, self.lam_scale_ = _init_coefs(
                X,
                method=self.init_method)

        if not self.auto_scale:
            self.lam_scale_ = 1.0

    def score(self, X_test, y=None):
        """Computes the score between cov/prec of sample covariance of X_test
        and X via 'score_metric'.

        Note: We want to maximize score so we return the negative error.

        Parameters
        ----------
        X_test : array-like, shape = [n_samples, n_features]
            Test data of which we compute the likelihood, where n_samples is
            the number of samples and n_features is the number of features.
            X_test is assumed to be drawn from the same distribution than
            the data used in fit (including centering).

        y : not used.

        Returns
        -------
        result : float or list of floats
            The negative of the min error between `self.covariance_` and
            the sample covariance of X_test.
        """
        if isinstance(self.precision_, list):
            print 'Warning: returning a list of scores.'

        S_test, lam_scale_test = _init_coefs(
                X_test,
                method=self.init_method)
        error = self.cov_error(S_test, score_metric=self.score_metric)

        # maximize score with -error
        return -error

    def cov_error(self, comp_cov, score_metric='frobenius'):
        """Computes the covariance error vs. comp_cov.

        Parameters
        ----------
        comp_cov : array-like, shape = (n_features, n_features)
            The precision to compare with.
            This should normally be the test sample covariance/precision.

        scaling : bool
            If True, the squared error norm is divided by n_features.
            If False (default), the squared error norm is not rescaled.

        score_metric : str
            The type of norm used to compute the error between the estimated
            self.precision, self.covariance and the reference `comp_cov`.
            Available error types:

            - 'frobenius' (default): sqrt(tr(A^t.A))
            - 'spectral': sqrt(max(eigenvalues(A^t.A))
            - 'kl': kl-divergence
            - 'quadratic': quadratic loss
            - 'log_likelihood': negative log likelihood

        squared : bool
            Whether to compute the squared error norm or the error norm.
            If True (default), the squared error norm is returned.
            If False, the error norm is returned.

        Returns
        -------
        The min error between `self.covariance_` and `comp_cov`.

        If self.precision_ is a list, returns errors for each matrix, otherwise
        returns a scalar.
        """
        if not isinstance(self.precision_, list):
            return _compute_error(comp_cov,
                                self.covariance_,
                                self.precision_,
                                score_metric)

        path_errors = []
        for lidx, lam in enumerate(self.path):
            path_errors.append(_compute_error(comp_cov,
                                            self.covariance_[lidx],
                                            self.precision_[lidx],
                                            score_metric))

        return np.array(path_errors)

    def ebic(self, gamma=0):
        """Compute EBIC scores for each model. If model is not "path" then
        returns a scalar score value. Uses an expected self.path_ parameter.

        See:
            Extended Bayesian Information Criteria for Gaussian Graphical Models
            R. Foygel and M. Drton
            NIPS 2010

        Parameters
        ----------
        gamma : (float) \in (0, 1)
            Choice of gamma=0 leads to classical BIC
            Positive gamma leads to stronger penalization of large graphs.

        Returns
        -------
        Scalar ebic score or list of ebic scores.
        """
        if not self.is_fitted:
            return

        if not isinstance(self.precision_, list):
            return metrics.ebic(self.sample_covariance_,
                                self.precision_,
                                self.n_samples,
                                self.n_features,
                                gamma=gamma)

        ebic_scores = []
        for lidx, lam in enumerate(self.path_):
            ebic_scores.append(metrics.ebic(
                    self.sample_covariance_,
                    self.precision_[lidx],
                    self.n_samples,
                    self.n_features,
                    gamma=gamma))

        return np.array(ebic_scores)

    def ebic_select(self, gamma=0):
        """Uses Extended Bayesian Information Criteria for model selection.

        Can only be used in path mode (doesn't really make sense otherwise).

        See:
            Extended Bayesian Information Criteria for Gaussian Graphical Models
            R. Foygel and M. Drton
            NIPS 2010

        Parameters
        ----------
        gamma : (float) \in (0, 1)
            Choice of gamma=0 leads to classical BIC
            Positive gamma leads to stronger penalization of large graphs.

        Returns
        -------
        Lambda index with best ebic score.  When multiple ebic scores are the
        same, returns the smallest lambda (largest index) with minimum score.
        """
        if not isinstance(self.precision_, list):
            raise NotImplementedError("EBIC requires multiple models to select from.")
            return

        if not self.is_fitted:
            return

        ebic_scores = self.ebic(gamma=gamma)
        min_indices = np.where(np.abs(ebic_scores - ebic_scores.min()) < 1e-10)
        return np.max(min_indices)
<commit_msg>More fixes for param change<commit_after>import numpy as np
from sklearn.base import BaseEstimator

from . import metrics

def _init_coefs(X, method='corrcoef'):
    if method == 'corrcoef':
        return np.corrcoef(X, rowvar=False), 1.0
    elif method == 'cov':
        init_cov = np.cov(X, rowvar=False)
        return init_cov, np.max(np.abs(np.triu(init_cov)))
    else:
        raise ValueError(("initialize_method must be 'corrcoef' or 'cov', "
            "passed \'{}\' .".format(method)))

def _compute_error(comp_cov, covariance_, precision_, score_metric='frobenius'):
    """Computes the covariance error vs. comp_cov.

    Parameters
    ----------
    comp_cov : array-like, shape = (n_features, n_features)
        The precision to compare with.
        This should normally be the test sample covariance/precision.

    scaling : bool
        If True, the squared error norm is divided by n_features.
        If False (default), the squared error norm is not rescaled.

    score_metric : str
        The type of norm used to compute the error between the estimated
        self.precision, self.covariance and the reference `comp_cov`.
        Available error types:

        - 'frobenius' (default): sqrt(tr(A^t.A))
        - 'spectral': sqrt(max(eigenvalues(A^t.A))
        - 'kl': kl-divergence
        - 'quadratic': quadratic loss
        - 'log_likelihood': negative log likelihood

    squared : bool
        Whether to compute the squared error norm or the error norm.
        If True (default), the squared error norm is returned.
        If False, the error norm is returned.
    """
    if score_metric == "frobenius":
        error = comp_cov - covariance_
        return np.sum(error ** 2)
    elif score_metric == "spectral":
        error = comp_cov - covariance_
        return np.amax(np.linalg.svdvals(np.dot(error.T, error)))
    elif score_metric == "kl":
        return metrics.kl_loss(comp_cov, precision_)
    elif score_metric == "quadratic":
        return metrics.quadratic_loss(comp_cov, precision_)
    elif score_metric == "log_likelihood":
        return -metrics.log_likelihood(comp_cov, precision_)
    else:
        raise NotImplementedError(("Must be frobenius, spectral, kl, "
                                   "quadratic, or log_likelihood"))

def _validate_path(path):
    """Sorts path values from largest to smallest.

    Will warn if path parameter was not already sorted.
    """
    if path is None:
        return None

    new_path = np.array(sorted(set(path), reverse=True))
    if new_path[0] != path[0]:
        print 'Warning: Path must be sorted largest to smallest.'

    return new_path

class InverseCovarianceEstimator(BaseEstimator):
    """
    Base class for inverse covariance estimators.

    Provides initialization method, metrics, scoring function,
    and ebic model selection.

    Parameters
    -----------
    score_metric : one of 'log_likelihood' (default), 'frobenius', 'spectral',
                  'kl', or 'quadratic'
        Used for computing self.score().

    init_method : one of 'corrcoef', 'cov'
        Computes initial covariance and scales lambda appropriately.

    auto_scale : bool
        If True, will compute self.lam_scale_ = max off-diagonal value when
        init_method='cov'.
        If false, then self.lam_scale_ = 1.
        lam_scale_ is used to scale user-supplied self.lam during fit.

    Attributes
    ----------
    covariance_ : 2D ndarray, shape (n_features, n_features)
        Estimated covariance matrix

        This can also be a len(path) list of
        2D ndarray, shape (n_features, n_features)
        (e.g., see mode='path' in QuicGraphLasso)

    precision_ : 2D ndarray, shape (n_features, n_features)
        Estimated pseudo-inverse matrix.

        This can also be a len(path) list of
        2D ndarray, shape (n_features, n_features)
        (e.g., see mode='path' in QuicGraphLasso)

    sample_covariance_ : 2D ndarray, shape (n_features, n_features)
        Estimated sample covariance matrix

    lam_scale_ : (float)
        Additional scaling factor on lambda (due to magnitude of
        sample_covariance_ values).
    """
    def __init__(self, score_metric='log_likelihood', init_method='cov',
                 auto_scale=True):
        self.score_metric = score_metric
        self.init_method = init_method
        self.auto_scale = auto_scale

        self.covariance_ = None  # assumes a matrix of a list of matrices
        self.precision_ = None  # assumes a matrix of a list of matrices

        # these must be updated upon self.fit()
        # the first 4 will be set if self.init_coefs is used.
        self.sample_covariance_ = None
        self.lam_scale_ = None
        self.n_samples = None
        self.n_features = None
        self.path_ = None
        self.is_fitted = False

        super(InverseCovarianceEstimator, self).__init__()

    def init_coefs(self, X):
        """Computes ...

        Initialize the following values:
            self.n_samples
            self.n_features
            self.sample_covariance_
            self.lam_scale_
        """
        self.n_samples, self.n_features = X.shape
        self.sample_covariance_, self.lam_scale_ = _init_coefs(
                X,
                method=self.init_method)

        if not self.auto_scale:
            self.lam_scale_ = 1.0

    def score(self, X_test, y=None):
        """Computes the score between cov/prec of sample covariance of X_test
        and X via 'score_metric'.

        Note: We want to maximize score so we return the negative error.

        Parameters
        ----------
        X_test : array-like, shape = [n_samples, n_features]
            Test data of which we compute the likelihood, where n_samples is
            the number of samples and n_features is the number of features.
            X_test is assumed to be drawn from the same distribution than
            the data used in fit (including centering).

        y : not used.

        Returns
        -------
        result : float or list of floats
            The negative of the min error between `self.covariance_` and
            the sample covariance of X_test.
        """
        if isinstance(self.precision_, list):
            print 'Warning: returning a list of scores.'

        S_test, lam_scale_test = _init_coefs(
                X_test,
                method=self.init_method)
        error = self.cov_error(S_test, score_metric=self.score_metric)

        # maximize score with -error
        return -error

    def cov_error(self, comp_cov, score_metric='frobenius'):
        """Computes the covariance error vs. comp_cov.

        May require self.path_

        Parameters
        ----------
        comp_cov : array-like, shape = (n_features, n_features)
            The precision to compare with.
            This should normally be the test sample covariance/precision.

        scaling : bool
            If True, the squared error norm is divided by n_features.
            If False (default), the squared error norm is not rescaled.

        score_metric : str
            The type of norm used to compute the error between the estimated
            self.precision, self.covariance and the reference `comp_cov`.
            Available error types:

            - 'frobenius' (default): sqrt(tr(A^t.A))
            - 'spectral': sqrt(max(eigenvalues(A^t.A))
            - 'kl': kl-divergence
            - 'quadratic': quadratic loss
            - 'log_likelihood': negative log likelihood

        squared : bool
            Whether to compute the squared error norm or the error norm.
            If True (default), the squared error norm is returned.
            If False, the error norm is returned.

        Returns
        -------
        The min error between `self.covariance_` and `comp_cov`.

        If self.precision_ is a list, returns errors for each matrix, otherwise
        returns a scalar.
        """
        if not isinstance(self.precision_, list):
            return _compute_error(comp_cov,
                                self.covariance_,
                                self.precision_,
                                score_metric)

        path_errors = []
        for lidx, lam in enumerate(self.path_):
            path_errors.append(_compute_error(comp_cov,
                                            self.covariance_[lidx],
                                            self.precision_[lidx],
                                            score_metric))

        return np.array(path_errors)

    def ebic(self, gamma=0):
        """Compute EBIC scores for each model. If model is not "path" then
        returns a scalar score value.

        May require self.path_

        See:
            Extended Bayesian Information Criteria for Gaussian Graphical Models
            R. Foygel and M. Drton
            NIPS 2010

        Parameters
        ----------
        gamma : (float) \in (0, 1)
            Choice of gamma=0 leads to classical BIC
            Positive gamma leads to stronger penalization of large graphs.

        Returns
        -------
        Scalar ebic score or list of ebic scores.
        """
        if not self.is_fitted:
            return

        if not isinstance(self.precision_, list):
            return metrics.ebic(self.sample_covariance_,
                                self.precision_,
                                self.n_samples,
                                self.n_features,
                                gamma=gamma)

        ebic_scores = []
        for lidx, lam in enumerate(self.path_):
            ebic_scores.append(metrics.ebic(
                    self.sample_covariance_,
                    self.precision_[lidx],
                    self.n_samples,
                    self.n_features,
                    gamma=gamma))

        return np.array(ebic_scores)

    def ebic_select(self, gamma=0):
        """Uses Extended Bayesian Information Criteria for model selection.

        Can only be used in path mode (doesn't really make sense otherwise).

        See:
            Extended Bayesian Information Criteria for Gaussian Graphical Models
            R. Foygel and M. Drton
            NIPS 2010

        Parameters
        ----------
        gamma : (float) \in (0, 1)
            Choice of gamma=0 leads to classical BIC
            Positive gamma leads to stronger penalization of large graphs.

        Returns
        -------
        Lambda index with best ebic score.  When multiple ebic scores are the
        same, returns the smallest lambda (largest index) with minimum score.
        """
        if not isinstance(self.precision_, list):
            raise NotImplementedError("EBIC requires multiple models to select from.")
            return

        if not self.is_fitted:
            return

        ebic_scores = self.ebic(gamma=gamma)
        min_indices = np.where(np.abs(ebic_scores - ebic_scores.min()) < 1e-10)
        return np.max(min_indices)
<|endoftext|>


<commit_before>import argparse

import six
import txaio
txaio.use_twisted()

from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from twisted.internet.error import ReactorNotRunning
from twisted.internet.task import LoopingCall

from autobahn.twisted.wamp import ApplicationSession
from autobahn.twisted.wamp import ApplicationRunner
from autobahn.twisted.util import sleep

import RPi.GPIO as GPIO
from Adafruit_QuadAlphanum import QuadAlphanum

@inlineCallbacks
def scrollText(disp, text):
    s = text + "    "
    for i in range(len(s)):
        disp.setMessage(s[i:i+4])
        yield sleep(.2)

class VotesListener(ApplicationSession):

    @inlineCallbacks
    def onJoin(self, details):
        self.log.info("Session joined: {details}", details=details)

        # the voting subject we will display and vote for
        subject = self.config.extra[u'subject']

        # our quad, alphanumeric display: https://www.adafruit.com/products/2157
        self._disp = QuadAlphanum(self.config.extra[u'i2c_address'])
        self._disp.clear()
        self._disp.setBrightness(int(round(self.config.extra[u'brightness'] * 15)))

        # display votes for subject on display
        def setVotes(votes):
            if votes < 10000:
                text = "{:0>4d}".format(votes)
            else:
                text = "MANY"
            self._disp.setMessage(text)

        # get notified of new votes
        def onVote(vote):
            if vote[u'subject'] == subject:
                setVotes(vote[u'votes'])

        yield self.subscribe(onVote, u'io.crossbar.demo.vote.onvote')

        # get notified of votes being reset
        @inlineCallbacks
        def onReset():
            self._disp.setMessage('****')
            yield sleep(.1)
            setVotes(0)

        yield self.subscribe(onReset, u'io.crossbar.demo.vote.onreset')

        @inlineCallbacks
        def displayNotice():
            yield scrollText(self._disp, "watching votes for {} ...".format(subject).upper())

            # get the current votes
            votes = yield self.call(u'io.crossbar.demo.vote.get')
            for vote in votes:
                if vote[u'subject'] == subject:
                    setVotes(vote[u'votes'])

        # every couple of secs, display a notice
        LoopingCall(displayNotice).start(20)

        # init GPIO
        GPIO.setwarnings(False)
        GPIO.setmode(GPIO.BCM)
        GPIO.cleanup()
        GPIO.setup(self.config.extra[u'button_pin'], GPIO.IN)

        self._button_state = False

        @inlineCallbacks
        def scan_buttons():
            new_state = GPIO.input(self.config.extra[u'button_pin']) == 1
            if new_state != self._button_state:
                if new_state:
                    votes = yield self.call(u'io.crossbar.demo.vote.vote', subject)
                    setVotes(votes)
                self._button_state = new_state

        # periodically scan buttons
        scanner = LoopingCall(scan_buttons)
        scanner.start(1./50.)

        self.log.info("Votes listener ready!")

    def onLeave(self, details):
        self.log.info("Session closed: {details}", details=details)
        self.disconnect()

    def onDisconnect(self):
        self.log.info("Connection closed")
        self._disp.clear()
        try:
            reactor.stop()
        except ReactorNotRunning:
            pass

if __name__ == '__main__':

    parser = argparse.ArgumentParser()

    parser.add_argument('-d', '--debug', action='store_true', help='Enable debug output.')
    parser.add_argument("--router", type=six.text_type, default=u"wss://demo.crossbar.io/ws", help='WAMP router URL.')
    parser.add_argument("--realm", type=six.text_type, default=u"crossbardemo", help='WAMP router realm.')

    args = parser.parse_args()

    if args.debug:
        txaio.start_logging(level='debug')
    else:
        txaio.start_logging(level='info')

    extra = {
        # the voting subject the display will show, and the button
        # will trigger voting for
        u'subject': u'Banana',

        # the button configuration (a BCM digital pin number is required,
        # see here https://pinout.xyz/)
        u'button_pin': 2,

        # the quad-alpha display hardware configuration
        u'i2c_address': 0x70,
        u'brightness': 1.,
    }

    runner = ApplicationRunner(url=args.router, realm=args.realm, extra=extra)
    runner.run(VotesListener)
<commit_msg>fixes<commit_after>import argparse

import six
import txaio
txaio.use_twisted()

from twisted.internet import reactor
from twisted.internet.defer import inlineCallbacks
from twisted.internet.error import ReactorNotRunning
from twisted.internet.task import LoopingCall

from autobahn.twisted.wamp import ApplicationSession
from autobahn.twisted.wamp import ApplicationRunner
from autobahn.twisted.util import sleep

import RPi.GPIO as GPIO
from Adafruit_QuadAlphanum import QuadAlphanum

@inlineCallbacks
def scrollText(disp, text):
    s = text + "    "
    for i in range(len(s)):
        disp.setMessage(s[i:i+4])
        yield sleep(.2)

class VotesListener(ApplicationSession):

    @inlineCallbacks
    def onJoin(self, details):
        self.log.info("Session joined: {details}", details=details)

        # the voting subject we will display and vote for
        subject = self.config.extra[u'subject']

        # our quad, alphanumeric display: https://www.adafruit.com/products/2157
        self._disp = QuadAlphanum(self.config.extra[u'i2c_address'])
        self._disp.clear()
        self._disp.setBrightness(int(round(self.config.extra[u'brightness'] * 15)))

        # display votes for subject on display
        def setVotes(votes):
            if votes < 10000:
                text = "{:0>4d}".format(votes)
            else:
                text = "MANY"
            self._disp.setMessage(text)

        # get notified of new votes
        def onVote(vote):
            if vote[u'subject'] == subject:
                setVotes(vote[u'votes'])

        yield self.subscribe(onVote, u'io.crossbar.demo.vote.onvote')

        # get notified of votes being reset
        @inlineCallbacks
        def onReset():
            self._disp.setMessage('****')
            yield sleep(.1)
            setVotes(0)

        yield self.subscribe(onReset, u'io.crossbar.demo.vote.onreset')

        @inlineCallbacks
        def displayNotice():
            yield scrollText(self._disp, "watching votes for {} ...".format(subject).upper())

            # get the current votes
            votes = yield self.call(u'io.crossbar.demo.vote.get')
            for vote in votes:
                if vote[u'subject'] == subject:
                    setVotes(vote[u'votes'])

        # every couple of secs, display a notice
        LoopingCall(displayNotice).start(20)

        # init GPIO
        GPIO.setwarnings(False)
        GPIO.setmode(GPIO.BCM)
        GPIO.cleanup()
        GPIO.setup(self.config.extra[u'button_pin'], GPIO.IN)

        self._button_state = False

        @inlineCallbacks
        def scan_buttons():
            new_state = GPIO.input(self.config.extra[u'button_pin']) == 1
            if new_state != self._button_state:
                self.log.info("Button state change: {new_state}", new_state=new_state)
                if new_state:
                    votes = yield self.call(u'io.crossbar.demo.vote.vote', subject)
                    setVotes(votes)
                self._button_state = new_state

        # periodically scan buttons
        scanner = LoopingCall(scan_buttons)
        scanner.start(1./50.)

        self.log.info("Votes listener ready!")

    def onLeave(self, details):
        self.log.info("Session closed: {details}", details=details)
        self.disconnect()

    def onDisconnect(self):
        self.log.info("Connection closed")
        self._disp.clear()
        try:
            reactor.stop()
        except ReactorNotRunning:
            pass

if __name__ == '__main__':

    parser = argparse.ArgumentParser()

    parser.add_argument('-d', '--debug', action='store_true', help='Enable debug output.')
    parser.add_argument("--router", type=six.text_type, default=u"wss://demo.crossbar.io/ws", help='WAMP router URL.')
    parser.add_argument("--realm", type=six.text_type, default=u"crossbardemo", help='WAMP router realm.')

    args = parser.parse_args()

    if args.debug:
        txaio.start_logging(level='debug')
    else:
        txaio.start_logging(level='info')

    extra = {
        # the voting subject the display will show, and the button
        # will trigger voting for
        u'subject': u'Banana',

        # the button configuration (a BCM digital pin number is required,
        # see here https://pinout.xyz/)
        u'button_pin': 26,

        # the quad-alpha display hardware configuration
        u'i2c_address': 0x70,
        u'brightness': 1.,
    }

    runner = ApplicationRunner(url=args.router, realm=args.realm, extra=extra)
    runner.run(VotesListener)
<|endoftext|>