tBuLi / symfit

Symbolic Fitting; fitting as it should be.
http://symfit.readthedocs.org
MIT License
233 stars 17 forks source link

Problem fitting plane #249

Closed egpbos closed 5 years ago

egpbos commented 5 years ago

I'm trying to fit a set of points to a plane, but I seem to be doing something wrong. I hope you can help me out. Here's what I'm trying to do:

import numpy as np
import symfit as sf

# generate points in a plane with some noise in the y direction
xyz = np.array((np.random.random(1000), np.random.normal(0, 0.01, 1000), np.random.random(1000)))

# parameters that characterize the plane (a point x0,y0,z0 and a normal vector a,b,c)
a, b, c, x0, y0, z0 = sf.parameters('a, b, c, x0, y0, z0')
# and the equation that describes the plane
x, y, z = sf.variables('x, y, z')
plane_model = {x: (x0 * a + y0 * b + z0 * c - y * b - z * c) / a}

plane_fit = sf.Fit(plane_model, x=xyz[0], y=xyz[1], z=xyz[2])
plane_fit_result = plane_fit.execute()

This gives me a big traceback:

---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/support.py in __get__(self, obj, objtype)
    213         try:
--> 214             return getattr(obj, self.cache_attr)
    215         except AttributeError:

AttributeError: 'Model' object has no attribute '_cached_numerical_components'

During handling of the above exception, another exception occurred:

AttributeError                            Traceback (most recent call last)
<ipython-input-21-e3b6fef50893> in <module>
----> 1 plane_fit_result = plane_fit.execute()

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/fit.py in execute(self, **minimize_options)
   1535         :return: FitResults instance
   1536         """
-> 1537         minimizer_ans = self.minimizer.execute(**minimize_options)
   1538         try: # to build covariance matrix
   1539             cov_matrix = minimizer_ans.covariance_matrix

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/minimizers.py in execute(self, **minimize_options)
    357 
    358     def execute(self, **minimize_options):
--> 359         return super(ScipyGradientMinimize, self).execute(jacobian=self.wrapped_jacobian, **minimize_options)
    360 
    361 class ScipyConstrainedMinimize(ScipyMinimize, ConstrainedMinimizer):

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/support.py in wrapped_func(*args, **kwargs)
    353                     else:
    354                         bound_args.arguments[param.name] = param.default
--> 355             return func(*bound_args.args, **bound_args.kwargs)
    356         return wrapped_func
    357 

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/minimizers.py in execute(self, bounds, jacobian, constraints, **minimize_options)
    294             constraints=constraints,
    295             jac=jacobian,
--> 296             **minimize_options
    297         )
    298         return self._pack_output(ans)

~/sw/miniconda3/lib/python3.6/site-packages/scipy/optimize/_minimize.py in minimize(fun, x0, args, method, jac, hess, hessp, bounds, constraints, tol, callback, options)
    593         return _minimize_cg(fun, x0, args, jac, callback, **options)
    594     elif meth == 'bfgs':
--> 595         return _minimize_bfgs(fun, x0, args, jac, callback, **options)
    596     elif meth == 'newton-cg':
    597         return _minimize_newtoncg(fun, x0, args, jac, hess, hessp, callback,

~/sw/miniconda3/lib/python3.6/site-packages/scipy/optimize/optimize.py in _minimize_bfgs(fun, x0, args, jac, callback, gtol, norm, eps, maxiter, disp, return_all, **unknown_options)
    968     else:
    969         grad_calls, myfprime = wrap_function(fprime, args)
--> 970     gfk = myfprime(x0)
    971     k = 0
    972     N = len(x0)

~/sw/miniconda3/lib/python3.6/site-packages/scipy/optimize/optimize.py in function_wrapper(*wrapper_args)
    298     def function_wrapper(*wrapper_args):
    299         ncalls[0] += 1
--> 300         return function(*(wrapper_args + args))
    301 
    302     return ncalls, function_wrapper

~/sw/miniconda3/lib/python3.6/site-packages/scipy/optimize/optimize.py in approx_fprime(xk, f, epsilon, *args)
    728 
    729     """
--> 730     return _approx_fprime_helper(xk, f, epsilon, args=args)
    731 
    732 

~/sw/miniconda3/lib/python3.6/site-packages/scipy/optimize/optimize.py in _approx_fprime_helper(xk, f, epsilon, args, f0)
    662     """
    663     if f0 is None:
--> 664         f0 = f(*((xk,) + args))
    665     grad = numpy.zeros((len(xk),), float)
    666     ei = numpy.zeros((len(xk),), float)

~/sw/miniconda3/lib/python3.6/site-packages/scipy/optimize/optimize.py in function_wrapper(*wrapper_args)
    298     def function_wrapper(*wrapper_args):
    299         ncalls[0] += 1
--> 300         return function(*(wrapper_args + args))
    301 
    302     return ncalls, function_wrapper

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/minimizers.py in wrapped_func(values)
    271         def wrapped_func(values):
    272             parameters = key2str(dict(zip(self.params, values)))
--> 273             return np.array(func(**parameters))
    274         return wrapped_func
    275 

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/support.py in wrapped_func(*args, **kwargs)
    353                     else:
    354                         bound_args.arguments[param.name] = param.default
--> 355             return func(*bound_args.args, **bound_args.kwargs)
    356         return wrapped_func
    357 

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/objectives.py in __call__(self, **parameters)
    149         jac_kwargs = key2str(parameters)
    150         jac_kwargs.update(key2str(self.independent_data))
--> 151         evaluated_func = self.model(**jac_kwargs)
    152 
    153         chi2 = [0 for _ in evaluated_func]

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/fit.py in __call__(self, *args, **kwargs)
    332         bound_arguments = self.__signature__.bind(*args, **kwargs)
    333         Ans = namedtuple('Ans', [var.name for var in self])
--> 334         return Ans(*self.eval_components(**bound_arguments.arguments))
    335 
    336     @keywordonly(dx=1e-8)

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/fit.py in eval_components(self, *args, **kwargs)
    294         :return: lambda functions of each of the components in model_dict, to be used in numerical calculation.
    295         """
--> 296         return [expr(*args, **kwargs) for expr in self.numerical_components]
    297 
    298     @abstractmethod

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/support.py in __get__(self, obj, objtype)
    215         except AttributeError:
    216             # Call the wrapped function with the obj instance as argument
--> 217             setattr(obj, self.cache_attr, self.fget(obj))
    218             return getattr(obj, self.cache_attr)
    219 

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/fit.py in numerical_components(self)
    455         model_dict, to be used in numerical calculation.
    456         """
--> 457         return [sympy_to_py(expr, self.independent_vars, self.params) for expr in self.values()]
    458 
    459 

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/fit.py in <listcomp>(.0)
    455         model_dict, to be used in numerical calculation.
    456         """
--> 457         return [sympy_to_py(expr, self.independent_vars, self.params) for expr in self.values()]
    458 
    459 

~/sw/miniconda3/lib/python3.6/site-packages/symfit/core/support.py in sympy_to_py(func, vars, params)
     89         first, then params.
     90     """
---> 91     return lambdify((vars + params), func, modules='numpy', dummify=False)
     92 
     93 def sympy_to_scipy(func, vars, params):

~/sw/miniconda3/lib/python3.6/site-packages/sympy/utilities/lambdify.py in lambdify(args, expr, modules, printer, use_imps, dummify)
    765         funcprinter = _EvaluatorPrinter(printer, dummify)
    766 
--> 767     funcstr = funcprinter.doprint(funcname, args, expr)
    768 
    769     funclocals = {}

~/sw/miniconda3/lib/python3.6/site-packages/sympy/utilities/lambdify.py in doprint(self, funcname, args, expr)
    975             args = [args]
    976 
--> 977         argstrs, expr = self._preprocess(args, expr)
    978 
    979         # Generate argument unpacking and final argument list

~/sw/miniconda3/lib/python3.6/site-packages/sympy/utilities/lambdify.py in _preprocess(self, args, expr)
   1037                 s = str(arg)
   1038             elif isinstance(arg, Basic) and arg.is_symbol:
-> 1039                 s = self._argrepr(arg)
   1040                 if dummify or not self._is_safe_ident(s):
   1041                     dummy = Dummy()

~/sw/miniconda3/lib/python3.6/site-packages/sympy/printing/codeprinter.py in doprint(self, expr, assign_to)
     98         self._number_symbols = set()
     99 
--> 100         lines = self._print(expr).splitlines()
    101 
    102         # format the output

~/sw/miniconda3/lib/python3.6/site-packages/sympy/printing/printer.py in _print(self, expr, **kwargs)
    285                 printmethod = '_print_' + cls.__name__
    286                 if hasattr(self, printmethod):
--> 287                     return getattr(self, printmethod)(expr, **kwargs)
    288             # Unknown object, fall back to the emptyPrinter.
    289             return self.emptyPrinter(expr)

~/sw/miniconda3/lib/python3.6/site-packages/sympy/printing/codeprinter.py in _print_Variable(self, expr)
    342 
    343     def _print_Variable(self, expr):
--> 344         return self._print(expr.symbol)
    345 
    346     def _print_Statement(self, expr):

AttributeError: 'Variable' object has no attribute 'symbol'

Any ideas what might be causing this?

It's well possible that the parameterization of the plane is wrong, but even so, should I get these errors?

pckroon commented 5 years ago

Hi,

at a glance your code should work. And if I recall correctly this error is due to a version mismatch between sympy and symfit. Could you 1) try to update both, and if that doesn't work 2) come back with the versions of both?

egpbos commented 5 years ago

Ah, thanks for the suggestion. It was not as simple as updating, because I was using conda and the problem seems to be that the newest conda-forge version of symfit was 0.4.6 (on macOS). Installing the symfit 0.5.0 from pypi indeed fixed things. Thanks!

egpbos commented 5 years ago

By the way, I don't know if this is expected, but 0.5.0 from pypi also pulls in scipy and toposort as dependencies, whereas the conda package (but 0.4.6) doesn't use these dependencies.

pckroon commented 5 years ago

Scipy is definitely a dependency for any version of symfit. I'm not sure when toposort was introduced. Either way, someone needs to look into the conda dist.