# Path: pyglm/components/latent.py
# class LatentVariables(Component):
#     """
#     Container for a set of latent variables, e.g. neuron types/locations
#     """
#     def __init__(self, model):
#         """
#         Go through the items in the model, each of which specifies a latent variable component
#         """
#         self.model = model
#         if 'latent' in model.keys():
#             self.latent_model = model['latent']
#         else:
#             self.latent_model = {}
# 
#         self.log_p = T.constant(0.0)
# 
#         # Enumerate and create latent variable component
#         self.latentlist = []
#         self.latentdict = {}
# 
#         for (k,v) in self.latent_model.items():
#             # Create the latent component
#             latent_component = create_latent_component(model, v)
#             self.log_p += latent_component.log_p
# 
#             # Add to the list of latent variable components
#             self.latentlist.append(latent_component)
#             self.latentdict[latent_component.name] = latent_component
# 
#     def get_variables(self):
#         v = {}
#         for (name, comp) in self.latentdict.items():
#             v[name] = comp.get_variables()
# 
#         return v
# 
#     def get_state(self):
#         st = {}
#         for (name, comp) in self.latentdict.items():
#             st[name] = comp.get_state()
# 
#         return st
# 
#     def sample(self, acc):
#         s = {}
#         for (name, comp) in self.latentdict.items():
#             s[name] = comp.sample(acc)
#         return s
# 
#     def set_data(self, data):
#         for comp in self.latentlist:
#             comp.set_data(data)
# 
#     # Allow consumers to access this container as a dict
#     def __getitem__(self, item):
#         return self.latentdict[item]
# 
# Path: pyglm/components/network.py
# class Network(Component):
#     """ The network component encapsulates the impulse responses, weights,
#     and adjacency matrix of the spiking interactions.
#     """
# 
#     def __init__(self, model, latent):
#         """ Initialize the filtered stim model
#         """
#         self.model = model
#         self.latent = latent
#         self.prms = model['network']
# 
#         # Keep track of the number of variables
#         self.n_vars = 0
# 
#         # Create the graph model for the adjacency matrix
#         self.graph = create_graph_component(model, latent)
# 
#         # Create the weight model for the weight matrix
#         self.weights = create_weight_component(model, latent)
# 
#         # Compute log probability
#         self.log_p = self.graph.log_p + self.weights.log_p
#         
#     def get_variables(self):
#         """ Get the theano variables associated with this model.
#         """
#         return {'graph' : self.graph.get_variables(),
#                 'weights' : self.weights.get_variables()}
#         
#     def get_state(self):
#         """ Get the state of the graph and weights
#         """
#         state = {'graph' : self.graph.get_state(),
#                  'weights' : self.weights.get_state()}
#         return state
#     
#     def set_data(self, data):
#         """ Set the shared memory variables that depend on the data
#         """
#         self.graph.set_data(data)
#         self.weights.set_data(data)
# 
#     def sample(self, acc):
#         """
#         return a sample of the variables
#                 """
#         return {'graph' : self.graph.sample(acc),
#                 'weights' : self.weights.sample(acc)}
# 
# 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) 


import numpy as np
from pyglm.components.latent import LatentVariables
from pyglm.components.network import Network
from glm import Glm
from pyglm.utils.theano_func_wrapper import seval


class Population:
    """
    Population connected GLMs.
    """
    def __init__(self, model):
        """
        Initialize the population of GLMs connected by a network.
        """
        self.model = model
        self.N = model['N']

        # Initialize a list of data sequences
        self.data_sequences = []

        # Initialize latent variables of the population

# Path: pyglm/components/latent.py
# class LatentVariables(Component):
#     """
#     Container for a set of latent variables, e.g. neuron types/locations
#     """
#     def __init__(self, model):
#         """
#         Go through the items in the model, each of which specifies a latent variable component
#         """
#         self.model = model
#         if 'latent' in model.keys():
#             self.latent_model = model['latent']
#         else:
#             self.latent_model = {}
# 
#         self.log_p = T.constant(0.0)
# 
#         # Enumerate and create latent variable component
#         self.latentlist = []
#         self.latentdict = {}
# 
#         for (k,v) in self.latent_model.items():
#             # Create the latent component
#             latent_component = create_latent_component(model, v)
#             self.log_p += latent_component.log_p
# 
#             # Add to the list of latent variable components
#             self.latentlist.append(latent_component)
#             self.latentdict[latent_component.name] = latent_component
# 
#     def get_variables(self):
#         v = {}
#         for (name, comp) in self.latentdict.items():
#             v[name] = comp.get_variables()
# 
#         return v
# 
#     def get_state(self):
#         st = {}
#         for (name, comp) in self.latentdict.items():
#             st[name] = comp.get_state()
# 
#         return st
# 
#     def sample(self, acc):
#         s = {}
#         for (name, comp) in self.latentdict.items():
#             s[name] = comp.sample(acc)
#         return s
# 
#     def set_data(self, data):
#         for comp in self.latentlist:
#             comp.set_data(data)
# 
#     # Allow consumers to access this container as a dict
#     def __getitem__(self, item):
#         return self.latentdict[item]
# 
# Path: pyglm/components/network.py
# class Network(Component):
#     """ The network component encapsulates the impulse responses, weights,
#     and adjacency matrix of the spiking interactions.
#     """
# 
#     def __init__(self, model, latent):
#         """ Initialize the filtered stim model
#         """
#         self.model = model
#         self.latent = latent
#         self.prms = model['network']
# 
#         # Keep track of the number of variables
#         self.n_vars = 0
# 
#         # Create the graph model for the adjacency matrix
#         self.graph = create_graph_component(model, latent)
# 
#         # Create the weight model for the weight matrix
#         self.weights = create_weight_component(model, latent)
# 
#         # Compute log probability
#         self.log_p = self.graph.log_p + self.weights.log_p
#         
#     def get_variables(self):
#         """ Get the theano variables associated with this model.
#         """
#         return {'graph' : self.graph.get_variables(),
#                 'weights' : self.weights.get_variables()}
#         
#     def get_state(self):
#         """ Get the state of the graph and weights
#         """
#         state = {'graph' : self.graph.get_state(),
#                  'weights' : self.weights.get_state()}
#         return state
#     
#     def set_data(self, data):
#         """ Set the shared memory variables that depend on the data
#         """
#         self.graph.set_data(data)
#         self.weights.set_data(data)
# 
#     def sample(self, acc):
#         """
#         return a sample of the variables
#                 """
#         return {'graph' : self.graph.sample(acc),
#                 'weights' : self.weights.sample(acc)}
# 
# 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) 


# Path: pyglm/population.py
import numpy as np
from pyglm.components.latent import LatentVariables
from pyglm.components.network import Network
from glm import Glm
from pyglm.utils.theano_func_wrapper import seval



class Population:
    """
    Population connected GLMs.
    """
    def __init__(self, model):
        """
        Initialize the population of GLMs connected by a network.
        """
        self.model = model
        self.N = model['N']

        # Initialize a list of data sequences
        self.data_sequences = []

        # Initialize latent variables of the population

        self.latent = LatentVariables(model)