# Path: pysaliency/optpy/jacobian.py
# class FunctionWithApproxJacobian(object):
#     def __init__(self, func, epsilon, verbose=True):
#         self._func = func
#         self.epsilon = epsilon
#         self.value_cache = {}
#         self.verbose = verbose
# 
#     def __call__(self, x, *args, **kwargs):
#         key = tuple(x)
#         if not key in self.value_cache:
#             self.log('.')
#             value = self._func(x, *args, **kwargs)
#             if np.any(np.isnan(value)):
#                 print("Warning! nan function value encountered at {0}".format(x))
#             self.value_cache[key] = value
#         return self.value_cache[key]
# 
#     def func(self, x, *args, **kwargs):
#         if self.verbose:
#             print(x)
#         return self(x, *args, **kwargs)
# 
#     def log(self, msg):
#         if self.verbose:
#             sys.stdout.write(msg)
#             sys.stdout.flush()
# 
#     def jac(self, x, *args, **kwargs):
#         self.log('G[')
#         x0 = np.asfarray(x)
#         #print x0
#         dxs = np.zeros((len(x0), len(x0) + 1))
#         for i in range(len(x0)):
#             dxs[i, i + 1] = self.epsilon
#         results = [self(*(x0 + dxs[:, i], ) + args, **kwargs) for i in range(len(x0) + 1)]
#         jac = np.zeros([len(x0), len(np.atleast_1d(results[0]))])
#         for i in range(len(x0)):
#             jac[i] = (results[i + 1] - results[0]) / self.epsilon
#         self.log(']')
#         return jac.transpose()


import numpy as np
import scipy.optimize
from .jacobian import FunctionWithApproxJacobian
        super(KeywordParameterManager, self).__init__(parameters, optimize, **initial_dict)


def wrap_parameter_manager(f, parameter_manager, additional_kwargs=None):
    if isinstance(parameter_manager, KeywordParameterManager):
        def new_f(x, *args, **kwargs):
            if args:
                raise ValueError('KeywordParameterManager can only be used with keyword! Try giving all arguments as keywords.')
            params = parameter_manager.extract_parameters(x, return_list = False)
            kwargs.update(params)
            if additional_kwargs:
                kwargs.update(additional_kwargs)
            return f(**kwargs)
        return new_f
    else:
        def new_f(x, *args, **kwargs):
            params = parameter_manager.extract_parameters(x, return_list = True)
            params.extend(args)
            if additional_kwargs:
                kwargs.update(additional_kwargs)
            return f(*params, **kwargs)
        return new_f


def minimize(f, parameter_manager_or_x0, optimize=None, args=(), kwargs=None, method='BFGS',
             jac=None,
             bounds=None,
             constraints=(),
             tol=None,
             options=None,

# Path: pysaliency/optpy/jacobian.py
# class FunctionWithApproxJacobian(object):
#     def __init__(self, func, epsilon, verbose=True):
#         self._func = func
#         self.epsilon = epsilon
#         self.value_cache = {}
#         self.verbose = verbose
# 
#     def __call__(self, x, *args, **kwargs):
#         key = tuple(x)
#         if not key in self.value_cache:
#             self.log('.')
#             value = self._func(x, *args, **kwargs)
#             if np.any(np.isnan(value)):
#                 print("Warning! nan function value encountered at {0}".format(x))
#             self.value_cache[key] = value
#         return self.value_cache[key]
# 
#     def func(self, x, *args, **kwargs):
#         if self.verbose:
#             print(x)
#         return self(x, *args, **kwargs)
# 
#     def log(self, msg):
#         if self.verbose:
#             sys.stdout.write(msg)
#             sys.stdout.flush()
# 
#     def jac(self, x, *args, **kwargs):
#         self.log('G[')
#         x0 = np.asfarray(x)
#         #print x0
#         dxs = np.zeros((len(x0), len(x0) + 1))
#         for i in range(len(x0)):
#             dxs[i, i + 1] = self.epsilon
#         results = [self(*(x0 + dxs[:, i], ) + args, **kwargs) for i in range(len(x0) + 1)]
#         jac = np.zeros([len(x0), len(np.atleast_1d(results[0]))])
#         for i in range(len(x0)):
#             jac[i] = (results[i + 1] - results[0]) / self.epsilon
#         self.log(']')
#         return jac.transpose()


# Path: pysaliency/optpy/optimization.py
import numpy as np
import scipy.optimize
from .jacobian import FunctionWithApproxJacobian

        super(KeywordParameterManager, self).__init__(parameters, optimize, **initial_dict)


def wrap_parameter_manager(f, parameter_manager, additional_kwargs=None):
    if isinstance(parameter_manager, KeywordParameterManager):
        def new_f(x, *args, **kwargs):
            if args:
                raise ValueError('KeywordParameterManager can only be used with keyword! Try giving all arguments as keywords.')
            params = parameter_manager.extract_parameters(x, return_list = False)
            kwargs.update(params)
            if additional_kwargs:
                kwargs.update(additional_kwargs)
            return f(**kwargs)
        return new_f
    else:
        def new_f(x, *args, **kwargs):
            params = parameter_manager.extract_parameters(x, return_list = True)
            params.extend(args)
            if additional_kwargs:
                kwargs.update(additional_kwargs)
            return f(*params, **kwargs)
        return new_f


def minimize(f, parameter_manager_or_x0, optimize=None, args=(), kwargs=None, method='BFGS',
             jac=None,
             bounds=None,
             constraints=(),
             tol=None,
             options=None,

             jac_approx = FunctionWithApproxJacobian,