Tutorial
========

Simple Example
--------------
The example below shows how easy it is to define a model that we could fit to. ::

  from symfit.api import Parameter, Variable
  
  a = Parameter()
  b = Parameter()
  x = Variable()
  model = a * x + b

Lets fit this model to some generated data. ::

  from symfit.api import Fit
  import numpy as np
  
  xdata = np.linspace(0, 100, 100) # From 0 to 100 in 100 steps
  a_vec = np.random.normal(15.0, scale=2.0, size=(100,))
  b_vec = np.random.normal(100.0, scale=2.0, size=(100,))
  ydata = a_vec * xdata + b_vec # Point scattered around the line 5 * x + 105
  
  fit = Fit(model, xdata, ydata)
  fit_result = fit.execute()

.. figure:: _static/linear_model_fit_data.png
   :width: 300px
   :alt: Linear Model Fit Data

Printing ``fit_result`` will give a full report on the values for every parameter, including the uncertainty, and quality of the fit.

Initial Guess
-------------
For fitting to work as desired you should always give a good initial guess for a parameter.
The ``Parameter`` object can therefore be initiated with the following keywords:

* ``value`` the initial guess value.
* ``min`` Minimal value for the parameter.
* ``max`` Maximal value for the parameter.
* ``fixed`` Fix the value of the parameter during the fitting to ``value``.

In the example above, we might change our ``Parameter``'s to the following after looking at a plot of the data::

  k = Parameter(value=4, min=3, max=6)

  l, m = parameters('b, c')
  l.value = 60
  l.fixed = True

Accessing the Results
---------------------
A call to ``Fit.execute()`` returns a ``FitResults`` instance. 
This object holds all information about the fit. 
The fitting process does not modify the ``Parameter`` objects. 
In the above example, ``k.value`` will still be ``4.0`` and not the value we obtain after fitting. To get the value of fit parameters we can do::

  >>> print(fit_result.params.a)
  >>> 14.66946...
  >>> print(fit_result.params.a_stdev)
  >>> 0.3367571...
  >>> print(fit_result.params.b)
  >>> 104.6558...
  >>> print(fit_result.params.b_stdev)
  >>> 19.49172...
  >>> print(fit_result.r_squared)
  >>> 0.950890866472

For more FitResults, see the API docs.

Evaluating the Model
--------------------
With these parameters, we could now evaluate the model with these parameters so we can make a plot of it.
In order to do this, we simply call the model with these values::

  import matplotlib.pyplot as plt
  
  y = model(x=xdata, a=fit_result.params.a, b=fit_result.params.b)
  plt.plot(xdata, y)
  plt.show()

.. figure:: _static/linear_model_fit.png
   :width: 300px
   :alt: Linear Model Fit
  
The model *has* to be called by keyword arguments to prevent any ambiguity. So the following does not work::

  y = model(xdata, fit_result.params.a, fit_result.params.b)
  
To make life easier, there is a nice shorthand notation to immediately use a fit result::

  y = model(x=xdata, **fit_result.params)
  
This unpacks the .params object as a dict. For more info view ParameterDict.

Named Models
------------

More complicated models are also relatively easy to deal with by using named models.
Let's try our luck with a bivariate normal distribution::

    from symfit import parameters, variables, exp, pi, sqrt

    x, y, p = variables('x, y, p')
    mu_x, mu_y, sig_x, sig_y, rho = parameters('mu_x, mu_y, sig_x, sig_y, rho')

    z = (x - mu_x)**2/sig_x**2 + (y - mu_y)**2/sig_y**2 - 2 * rho * (x - mu_x) * (y - mu_y)/(sig_x * sig_y)
    model = {p: exp(- z / (2 * (1 - rho**2))) / (2 * pi * sig_x * sig_y * sqrt(1 - rho**2))}

    fit = Fit(model, x=xdata, y=ydata, p=pdata)

By using the magic of named models, the flow of information is still very clear, even with such a complicated function.

This syntax also supports vector valued functions::

    model = {y_1: a * x**2, y_2: 2 * x * b}

One thing to note about such models is that now ``model(x=xdata)`` obviously no longer works as ``type(model) == dict``.
There is a prevered way to resolve this. If any kind of fitting object has been initiated, it will have a ``.model`` atribute
containing an instance of ``Model``. This can again be called::

    model = {y_1: a * x**2, y_2: 2 * x * b}
    fit = Fit(model, x=xdata)
    fit_result = fit.execute()

    y_1, y_2 = fit.model(x=xdata, **fit_result.params)

This returns a tuple with the components evaluated so through the magic of tuple unpacking``y_1`` and ``y_2`` contain the
evaluated fit. Nice!

If for some reason no ``Fit`` is initiated you can make a Model object yourself::

    from symfit import Model

    model_dict = {y_1: a * x**2, y_2: 2 * x * b}
    model = Model.from_dict(model_dict)

    y_1, y_2 = fit.model(x=xdata, a=2.4, b=0.1)

symfit exposes sympy.api
------------------------

``symfit`` exposes the sympy api as well, so mathematical expressions such as ``exp``, ``sin`` and ``pi`` are importable
from ``symfit`` as well. For more, read the `sympy docs
<http://docs.sympy.org>`_.