Add new Movement Models ----------------------- Making a new movement model involves the following steps: Make the code ^^^^^^^^^^^^^ Make a new file with an abbreviation of the movement model's name. Any movement model must be a new class named ``Model`` that inherits from ``ollin.MovementModel``. A name for the class must be specified as an attribute:: from ollin import MovementModel class Model(MovementModel): name = 'New Movement Model' Movement behaviour is implemented in the method ``generate_movement``. This method must have as arguments: 1. The model instance (self). 2. An array with initial positions. 3. A :py:obj:`.Site` in which motion is to take place. 4. Number of steps to simulate. 5. Mean velocity to try to achieve. And it must return an array of shape ``[num, steps, 2]`` where ``num`` is the number of initial positions contained in the initial positions array. See :py:class:`.MovementModel` for full reference. For example:: from ollin import MovementModel import numpy as np class Model(MovementModel): name = 'Random Movement' def generate_movement(self, initial_position, site, steps, velocity): num = len(initial_position) movement = np.random.random([num, steps, 2]) return movement .. warning:: Note that velocity and site arguments are not used in this mock version of a movement model. This is perfectly fine and will not break any of the functionalities, although will probably lead to a bad movement model. .. note:: The name for the ``velocity`` argument might suggest that movement generated by the model must have mean velocity equal to ``velocity``. Although this would be desirable, it might not be possible, and so models have to go through a calibration phase so that desired mean velocity is obtained. See :any:`velocity-calibration`. If the movement model you wish to write depends on some parameters, please specify default parameters at the class definition inside the ``default_parameters`` dictionary, inside the ``movement`` key. This parameters can be overwritten at instantiation, and are be available at ``self.parameters``. For example:: class Model(MovementModel): name = 'Random Movement' default_parameters = { 'movement': { 'alpha': 0.4 } } def generate_movement(self, initial_position, site, steps, velocity): movement_parameters = self.parameters['movement'] alpha = movement_parameters['alpha'] num = len(initial_position) movement = np.random.binomial(1, alpha, shape=[num, steps, 2]) return movement Efficiency Considerations ^^^^^^^^^^^^^^^^^^^^^^^^^ Movement simulation is generally a computationally expensive operation. Hence a careful though on its implementation is often necessary. Bare Python functions have large overhead due to the nature of the language, but there are many paths to greater efficiency: :numpy: The fundamental numerical python package. Numpy routines handle large amounts of operations with optimized code, reducing the computing time in considerable amounts. Numpy arrays and operations are used through Ollin and are heavily recommended for any Movement Model implementation. Check this guide_ to get started with Numpy. .. _guide: https://docs.scipy.org/doc/numpy-1.15.1/user/quickstart.html :numba: This wonderful library allows to make optimized compiled versions of python functions while still having a simple interface and relative ease-of-use. Many Numpy operations are compatible with Numba compilation, but it does not support arbitrary functions. Check numba_ documentation on how to convert your functions into a optimized version and to consult valid operations. .. _numba: http://numba.pydata.org/numba-doc/latest/user/5minguide.html :cython: Cython_ is a superset of Python language that compiles to optimized C code that can be called from Python. .. _Cython: https://cython.readthedocs.io/en/latest/src/tutorial/cython_tutorial.html Calibration ^^^^^^^^^^^ In order to generate motion with the desired mean speed, or to generate scenarios with a given home range or occupancy, the model must be calibrated before use. For more details on calibration see :any:`calibration`. To calibrate the model open a new python terminal (or a jupyter notebook), import your model and make a new instance:: >> import ollin >> from new_model import Model >> model = Model() Ollin has a calibration tool that can be run in the following fashion:: >> calibrated_model, calibrated_parameters = ollin.calibrate(model) This function will run many simulations so be prepared to wait. If you wish to have some feedback on progress configure logging before running calibrate:: >> import logging >> logging.basicConfig(level=logging.INFO) The calibration procedure can be configured, see :py:mod:`.calibration.config` to consult all settings. .. warning:: Calibration simulations are executed in all available cores of host computer and will consume large amounts of memory. We recomend stopping other processes before calibration. With the results of the simulations, the parameters are fitted to the generated data and returned in a dictionary. Use these values to adjust the default parameters in the code file. So if:: >> calibrated_parameters { 'home_range': { 'alpha': 59.02095500748234, 'exponent': 1.903072119815655 }, 'density': { 'hr_exp': 0.8441724458551622, 'density_exp': 0.8501718469622543, 'alpha': 4.950773510732457, 'niche_size_exp': 0.8760285191282491 }, 'velocity': { 'alpha': 0.002223569915673946, 'beta': 1.013044346939526 } } Then edit the file to:: from ollin import MovementModel import numpy as np class Model(MovementModel): name = 'Random Movement' default_parameters = { 'movement': { 'alpha': 0.4 }, 'home_range': { 'alpha': 59.02095500748234, 'exponent': 1.903072119815655 }, 'density': { 'hr_exp': 0.8441724458551622, 'density_exp': 0.8501718469622543, 'alpha': 4.950773510732457, 'niche_size_exp': 0.8760285191282491 }, 'velocity': { 'alpha': 0.002223569915673946, 'beta': 1.013044346939526 } } def generate_movement(self, initial_position, site, steps, velocity): movement_parameters = self.parameters['movement'] alpha = movement_parameters['alpha'] num = len(initial_position) movement = np.random.binomial(1, alpha, shape=[num, steps, 2]) return movement The model is now calibrated. Add file to models library ^^^^^^^^^^^^^^^^^^^^^^^^^^ Once the model is calibrated, fork the repository_ and move the file to the directory ``ollin/movement_models/``. Reinstall Ollin using the setup script (see :any:`installation`) and use the model freely! Don't forget to send the pull request. .. _repository: https://github.com/mbsantiago/ollin