# Path: pyglm/utils/theano_func_wrapper.py
# def seval(expr, syms, vals, defaults=None, givens=[]):
#     """
#     Evaluate the symbolic expression which depends on a set of symbolic variables,
#     given a set of variable bindings.
#     expr : Theano variable to be evaluated
#     syms : dictionary of symbolic variables
#     vals : dictionary of values to assign to the symbolic vars in syms
#            key structure should mimic that of syms
#     
#     defaults : an optional dictionary providing backup values for 
#                syms keys not found in vals.
#     """
#     # Look for a function handle corresponding to this expression with these givens
#     hash_value = lambda v: hashlib.sha1(v).hexdigest() if \
#                            isinstance(v, np.ndarray) else v
#     hashable_givens = tuple(map(lambda (k,v): (k, hash_value(v)), givens))
#     hashable_syms = tuple(map(lambda v: hash_value(v), _flatten(syms)))
#     key = (expr, hashable_syms, hashable_givens)
#     if key in _func_cache.keys():
#         #print "DBG: Found key %s in cache" % str(key)
#         f = _func_cache[key]
#         #print "DBG: Calling expr: %s" % str(expr)
#     else:
#         # Create a callable theano function and cache it
#         # print "DBG: Compiling expr: %s" % str(expr)
#         sargs = _flatten(syms)
#         if len(givens) == 0:
#             # print "DBG: Compiling expression %s with no givens" % expr
#             f = theano.function(sargs, expr,
#                                 on_unused_input='ignore')
#         else:
#             # print "DBG: Compiling expression %s with givens: %s" % (expr, str(givens))
#             f = theano.function(sargs, expr,
#                                 givens=givens,
#                                 on_unused_input='ignore')
#         _func_cache[key] = f
#     
#     # Easiest thing to do is call the function with all symbolic variables
#     args = _extract_vals(syms,vals,defaults)
#     return f(*args) 
# 
# def _flatten(d):
#     """ Pack a hierarchical dictionary of variables into a list
#         Sorting is important as it ensures the function is called with 
#         the inputs in the same order each time!
#     """
#     l = []
#     # This sorting is important!
#     for (k,v) in sorted(d.items(), key=lambda t: t[0]):
#         if isinstance(v, dict):
#             lv = _flatten(v)
#             for v2 in lv:
#                 l.append(v2)
#         else:
#             l.append(v)
#     return l
# 
# Path: pyglm/components/graph.py
# class CompleteGraphModel(Component):
#     def __init__(self, model):
#         """ Initialize the filtered stim model
#         """
#         self.model = model
#         N = model['N']
# 
#         # Define complete adjacency matrix
#         self.A = T.ones((N, N))
# 
#         # Define log probability
#         self.log_p = T.constant(0.0)
# 
#     def get_state(self):
#         return {'A': self.A}
# 
# Path: pyglm/inference/smart_init.py
# def initialize_with_data(population, data, x0, Ns=None):
#     """ Initialize the parameters x0 with smart draws from the data
#     """
#     initialize_stim_with_sta(population, data, x0, Ns=Ns)
#     initialize_with_dense_graph(population, data, x0)
#     # initialize_with_no_coupling(population, data, x0)


import copy
import scipy.optimize as opt
from pyglm.utils.theano_func_wrapper import seval, _flatten
from pyglm.utils.packvec import *
from pyglm.utils.grads import *
from pyglm.components.graph import CompleteGraphModel
from pyglm.inference.smart_init import initialize_with_data

    print "Computing gradient of the prior w.r.t. the differentiable GLM parameters"
    g_glm_logprior, _ = grad_wrt_list(glm.log_prior, _flatten(glm_syms))

    print "Computing gradient of the GLM likelihood w.r.t. the differentiable GLM parameters"
    g_glm_ll, _ = grad_wrt_list(glm.ll, _flatten(glm_syms))

    # TODO: Replace this with a function that just gets the shapes?
    x0 = population.sample()
    nvars = population.extract_vars(x0, 0)
    dnvars = get_vars(glm_syms, nvars['glm'])
    _,glm_shapes = packdict(dnvars)

    # Private function to compute the log probability and its gradient
    # with respect to a set of parameters
    def nlp(x_glm_vec, x):
        """
        Helper function to compute the negative log posterior for a given set
        of GLM parameters. The parameters are passed in as a vector.
        """
        x_glm = unpackdict(x_glm_vec, glm_shapes)
        set_vars(glm_syms, x['glm'], x_glm)
        lp = seval(glm.log_prior,
                   syms,
                   x)

        # Add the likelihood of each data sequence
        for data in population.data_sequences:
            # Set the data
            population.set_data(data)

# Path: pyglm/utils/theano_func_wrapper.py
# def seval(expr, syms, vals, defaults=None, givens=[]):
#     """
#     Evaluate the symbolic expression which depends on a set of symbolic variables,
#     given a set of variable bindings.
#     expr : Theano variable to be evaluated
#     syms : dictionary of symbolic variables
#     vals : dictionary of values to assign to the symbolic vars in syms
#            key structure should mimic that of syms
#     
#     defaults : an optional dictionary providing backup values for 
#                syms keys not found in vals.
#     """
#     # Look for a function handle corresponding to this expression with these givens
#     hash_value = lambda v: hashlib.sha1(v).hexdigest() if \
#                            isinstance(v, np.ndarray) else v
#     hashable_givens = tuple(map(lambda (k,v): (k, hash_value(v)), givens))
#     hashable_syms = tuple(map(lambda v: hash_value(v), _flatten(syms)))
#     key = (expr, hashable_syms, hashable_givens)
#     if key in _func_cache.keys():
#         #print "DBG: Found key %s in cache" % str(key)
#         f = _func_cache[key]
#         #print "DBG: Calling expr: %s" % str(expr)
#     else:
#         # Create a callable theano function and cache it
#         # print "DBG: Compiling expr: %s" % str(expr)
#         sargs = _flatten(syms)
#         if len(givens) == 0:
#             # print "DBG: Compiling expression %s with no givens" % expr
#             f = theano.function(sargs, expr,
#                                 on_unused_input='ignore')
#         else:
#             # print "DBG: Compiling expression %s with givens: %s" % (expr, str(givens))
#             f = theano.function(sargs, expr,
#                                 givens=givens,
#                                 on_unused_input='ignore')
#         _func_cache[key] = f
#     
#     # Easiest thing to do is call the function with all symbolic variables
#     args = _extract_vals(syms,vals,defaults)
#     return f(*args) 
# 
# def _flatten(d):
#     """ Pack a hierarchical dictionary of variables into a list
#         Sorting is important as it ensures the function is called with 
#         the inputs in the same order each time!
#     """
#     l = []
#     # This sorting is important!
#     for (k,v) in sorted(d.items(), key=lambda t: t[0]):
#         if isinstance(v, dict):
#             lv = _flatten(v)
#             for v2 in lv:
#                 l.append(v2)
#         else:
#             l.append(v)
#     return l
# 
# Path: pyglm/components/graph.py
# class CompleteGraphModel(Component):
#     def __init__(self, model):
#         """ Initialize the filtered stim model
#         """
#         self.model = model
#         N = model['N']
# 
#         # Define complete adjacency matrix
#         self.A = T.ones((N, N))
# 
#         # Define log probability
#         self.log_p = T.constant(0.0)
# 
#     def get_state(self):
#         return {'A': self.A}
# 
# Path: pyglm/inference/smart_init.py
# def initialize_with_data(population, data, x0, Ns=None):
#     """ Initialize the parameters x0 with smart draws from the data
#     """
#     initialize_stim_with_sta(population, data, x0, Ns=Ns)
#     initialize_with_dense_graph(population, data, x0)
#     # initialize_with_no_coupling(population, data, x0)


# Path: pyglm/inference/coord_descent.py
import copy
import scipy.optimize as opt
from pyglm.utils.theano_func_wrapper import seval, _flatten
from pyglm.utils.packvec import *
from pyglm.utils.grads import *
from pyglm.components.graph import CompleteGraphModel
from pyglm.inference.smart_init import initialize_with_data


    print "Computing gradient of the prior w.r.t. the differentiable GLM parameters"
    g_glm_logprior, _ = grad_wrt_list(glm.log_prior, _flatten(glm_syms))

    print "Computing gradient of the GLM likelihood w.r.t. the differentiable GLM parameters"
    g_glm_ll, _ = grad_wrt_list(glm.ll, _flatten(glm_syms))

    # TODO: Replace this with a function that just gets the shapes?
    x0 = population.sample()
    nvars = population.extract_vars(x0, 0)
    dnvars = get_vars(glm_syms, nvars['glm'])
    _,glm_shapes = packdict(dnvars)

    # Private function to compute the log probability and its gradient
    # with respect to a set of parameters
    def nlp(x_glm_vec, x):
        """
        Helper function to compute the negative log posterior for a given set
        of GLM parameters. The parameters are passed in as a vector.
        """
        x_glm = unpackdict(x_glm_vec, glm_shapes)
        set_vars(glm_syms, x['glm'], x_glm)
        lp = seval(glm.log_prior,
                   syms,
                   x)

        # Add the likelihood of each data sequence
        for data in population.data_sequences:
            # Set the data
            population.set_data(data)

            lp += seval(glm.ll,