# Path: epistasis/matrix.py
# def get_model_matrix(binary_genotypes, sites, model_type='global'):
#     """Get a model matrix for a given set of genotypes and coefficients.
# 
#     Parameters
#     ----------
#     binary_genotypes : list or array
#         List of genotypes in their binary representation (see
#         gpmap.utils.genotypes_to_binary)
# 
#     sites : list
#         List of epistatic interaction sites.
# 
#     model_type : string
#         Type of epistasis model (global/Hadamard, local/Biochemical).
#     """
#     # Convert sites to array of arrays
#     sites = np.array([np.array(s) for s in sites],dtype=object)
#     
#     # Encode genotypes
#     encoded_vector = encode_vectors(binary_genotypes, model_type=model_type)
# 
#     # Build matrix.
#     X = build_model_matrix(encoded_vector, sites)
#     return X
# 
# Path: epistasis/models/utils.py
# def arghandler(method):
#     """Points methods to argument handlers. Assumes each argument has a
#     corresponding method attached to the object named "_{argument}". These
#     methods given default values to arguments.
# 
#     Ignores self and kwargs
#     """
#     @wraps(method)
#     def inner(self, *args, **kwargs):
#         # Get method name
#         name = method.__name__
# 
#         # Inspect function for arguments to update.
#         out = inspect.signature(method)
# 
#         # Construct kwargs from signature.
#         kws = {key: val.default for key, val in out.parameters.items()}
#         kws.pop('self')
# 
#         # Try to remove kwargs.
#         try:
#             kws.pop('kwargs')
#         except:
#             pass
# 
#         # Update kwargs with user specified kwargs.
#         kws.update(**kwargs)
# 
#         # Handle each argument
#         for arg in kws:
#             # Get handler function.
#             handler_name = "_{}".format(arg)
#             handler = getattr(self, handler_name)
#             kws[arg] = handler(data=kws[arg], method=name)
# 
#         return method(self, **kws)
#     return inner


import numpy as np
import pandas as pd
import lmfit
from sklearn.base import BaseEstimator, RegressorMixin
from epistasis.stats import pearson
from ..mapping import EpistasisMap, encoding_to_sites
from .base import BaseModel
from epistasis.matrix import get_model_matrix
from .utils import arghandler
        # Residual function to minimize.
        def residual(params, func, y=None):
            # Fit model
            parvals = list(params.values())
            ymodel = func(parvals)

            # Store items in case of error.
            nonlocal last_residual_set
            last_residual_set = (params, ymodel)

            return y - ymodel

        y = self.gpm.phenotypes

        # Minimize the above residual function.
        self.results = lmfit.minimize(
            residual, self.parameters,
            args=[self.functional_form],
            kws={'y': y})

        # Set parameters fitted by model.
        self.parameters = self.results.params

        return self

    def fit_transform(self, X=None, y=None, **kwargs):
        """Same as calling fit in ensemble model.
        """
        self.fit(X=X, y=y, **kwargs)


# Path: epistasis/matrix.py
# def get_model_matrix(binary_genotypes, sites, model_type='global'):
#     """Get a model matrix for a given set of genotypes and coefficients.
# 
#     Parameters
#     ----------
#     binary_genotypes : list or array
#         List of genotypes in their binary representation (see
#         gpmap.utils.genotypes_to_binary)
# 
#     sites : list
#         List of epistatic interaction sites.
# 
#     model_type : string
#         Type of epistasis model (global/Hadamard, local/Biochemical).
#     """
#     # Convert sites to array of arrays
#     sites = np.array([np.array(s) for s in sites],dtype=object)
#     
#     # Encode genotypes
#     encoded_vector = encode_vectors(binary_genotypes, model_type=model_type)
# 
#     # Build matrix.
#     X = build_model_matrix(encoded_vector, sites)
#     return X
# 
# Path: epistasis/models/utils.py
# def arghandler(method):
#     """Points methods to argument handlers. Assumes each argument has a
#     corresponding method attached to the object named "_{argument}". These
#     methods given default values to arguments.
# 
#     Ignores self and kwargs
#     """
#     @wraps(method)
#     def inner(self, *args, **kwargs):
#         # Get method name
#         name = method.__name__
# 
#         # Inspect function for arguments to update.
#         out = inspect.signature(method)
# 
#         # Construct kwargs from signature.
#         kws = {key: val.default for key, val in out.parameters.items()}
#         kws.pop('self')
# 
#         # Try to remove kwargs.
#         try:
#             kws.pop('kwargs')
#         except:
#             pass
# 
#         # Update kwargs with user specified kwargs.
#         kws.update(**kwargs)
# 
#         # Handle each argument
#         for arg in kws:
#             # Get handler function.
#             handler_name = "_{}".format(arg)
#             handler = getattr(self, handler_name)
#             kws[arg] = handler(data=kws[arg], method=name)
# 
#         return method(self, **kws)
#     return inner


# Path: epistasis/models/ensemble.py
import numpy as np
import pandas as pd
import lmfit
from sklearn.base import BaseEstimator, RegressorMixin
from epistasis.stats import pearson
from ..mapping import EpistasisMap, encoding_to_sites
from .base import BaseModel
from epistasis.matrix import get_model_matrix
from .utils import arghandler

        # Residual function to minimize.
        def residual(params, func, y=None):
            # Fit model
            parvals = list(params.values())
            ymodel = func(parvals)

            # Store items in case of error.
            nonlocal last_residual_set
            last_residual_set = (params, ymodel)

            return y - ymodel

        y = self.gpm.phenotypes

        # Minimize the above residual function.
        self.results = lmfit.minimize(
            residual, self.parameters,
            args=[self.functional_form],
            kws={'y': y})

        # Set parameters fitted by model.
        self.parameters = self.results.params

        return self

    def fit_transform(self, X=None, y=None, **kwargs):
        """Same as calling fit in ensemble model.
        """
        self.fit(X=X, y=y, **kwargs)


    @arghandler