diff --git a/bob/bio/base/algorithm/Algorithm.py b/bob/bio/base/algorithm/Algorithm.py
new file mode 100644
index 0000000000000000000000000000000000000000..6431a923d3f08f3437400fa9e4eec0058dcc8081
--- /dev/null
+++ b/bob/bio/base/algorithm/Algorithm.py
@@ -0,0 +1,204 @@
+#!/usr/bin/env python
+# vim: set fileencoding=utf-8 :
+# @author: Manuel Guenther <Manuel.Guenther@idiap.ch>
+# @date: Tue Oct  2 12:12:39 CEST 2012
+#
+# Copyright (C) 2011-2012 Idiap Research Institute, Martigny, Switzerland
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, version 3 of the License.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+import numpy
+import os
+from .. import utils
+
+class Algorithm:
+  """This is the base class for all biometric recognition algorithms.
+  It defines the minimum requirements for all derived algorithm classes.
+  """
+
+  def __init__(
+      self,
+      performs_projection = False, # enable if your tool will project the features
+      requires_projector_training = True, # by default, the projector needs training, if projection is enabled
+      split_training_features_by_client = False, # enable if your projector training needs the training files sorted by client
+      use_projected_features_for_enrollment = True, # by default, the enroller used projected features for enrollment, if projection is enabled.
+      requires_enroller_training = False, # enable if your enroller needs training
+
+      multiple_model_scoring = 'average', # by default, compute the average between several models and the probe
+      multiple_probe_scoring = 'average', # by default, compute the average between the model and several probes
+      **kwargs                            # parameters from the derived class that should be reported in the __str__() function
+  ):
+    """Initializes the Tool.
+    Call this constructor in derived class implementations.
+    If your derived tool performs feature projection, please register this here.
+    If it needs training for the projector or the enroller, please set this here, too.
+    """
+
+    self.performs_projection = performs_projection
+    self.requires_projector_training = performs_projection and requires_projector_training
+    self.split_training_features_by_client = split_training_features_by_client
+    self.use_projected_features_for_enrollment = performs_projection and use_projected_features_for_enrollment
+    self.requires_enroller_training = requires_enroller_training
+    self.m_model_fusion_function = utils.score_fusion_strategy(multiple_model_scoring)
+    self.m_probe_fusion_function = utils.score_fusion_strategy(multiple_probe_scoring)
+    self._kwargs = kwargs
+    self._kwargs.update({'multiple_model_scoring':multiple_model_scoring, 'multiple_probe_scoring':multiple_probe_scoring})
+
+
+  def __str__(self):
+    """This function returns all parameters of this class (and its derived class)."""
+    return "%s(%s)" % (str(self.__class__), ", ".join(["%s=%s" % (key, value) for key,value in self._kwargs.items() if value is not None]))
+
+
+  def enroll(self, enroll_features):
+    """This function will enroll and return the model from the given list of features.
+    It must be overwritten by derived classes.
+    """
+    raise NotImplementedError("Please overwrite this function in your derived class")
+
+
+  def score(self, model, probe):
+    """This function will compute the score between the given model and probe.
+    It must be overwritten by derived classes.
+    """
+    raise NotImplementedError("Please overwrite this function in your derived class")
+
+
+  def score_for_multiple_models(self, models, probe):
+    """This function computes the score between the given model list and the given probe.
+    In this base class implementation, it computes the scores for each model using the 'score' method,
+    and fuses the scores using the fusion method specified in the constructor of this class.
+    Usually this function is called from derived class 'score' functions."""
+    if isinstance(models, list):
+      return self.m_model_fusion_function([self.score(model, probe) for model in models])
+    elif isinstance(models, numpy.ndarray):
+      return self.m_model_fusion_function([self.score(models[i,:], probe) for i in range(models.shape[0])])
+    else:
+      raise ValueError("The model does not have the desired format (list, array, ...)")
+
+
+  def score_for_multiple_probes(self, model, probes):
+    """This function computes the score between the given model and the given probe files.
+    In this base class implementation, it computes the scores for each probe file using the 'score' method,
+    and fuses the scores using the fusion method specified in the constructor of this class."""
+    if isinstance(probes, list):
+      return self.m_probe_fusion_function([self.score(model, probe) for probe in probes])
+    else:
+      # only one probe feature -> use the default scoring function
+      return self.score(model, probes)
+
+
+  ############################################################
+  ### Special functions that might be overwritten on need
+  ############################################################
+
+  def write_feature(self, feature, feature_file):
+    """Saves the given *projected* feature to a file with the given name.
+    In this base class implementation:
+
+    - If the given feature has a 'save' attribute, it calls feature.save(bob.io.base.HDF5File(feature_file), 'w').
+      In this case, the given feature_file might be either a file name or a bob.io.base.HDF5File.
+    - Otherwise, it uses bob.io.base.save to do that.
+
+    If you have a different format, please overwrite this function.
+
+    Please register 'performs_projection = True' in the constructor to enable this function.
+    """
+    utils.save(feature, feature_file)
+
+
+  def read_feature(self, feature_file):
+    """Reads the *projected* feature from file.
+    In this base class implementation, it uses bob.io.base.load to do that.
+    If you have different format, please overwrite this function.
+
+    Please register 'performs_projection = True' in the constructor to enable this function.
+    """
+    return utils.load(feature_file)
+
+
+  def write_model(self, model, model_file):
+    """Saves the enrolled model to the given file.
+    In this base class implementation:
+
+    - If the given model has a 'save' attribute, it calls model.save(bob.io.base.HDF5File(model_file), 'w').
+      In this case, the given model_file might be either a file name or a bob.io.base.HDF5File.
+    - Otherwise, it uses bob.io.base.save to do that.
+
+    If you have a different format, please overwrite this function.
+    """
+    utils.save(model, model_file)
+
+
+  def read_model(self, model_file):
+    """Loads the enrolled model from file.
+    In this base class implementation, it uses bob.io.base.load to do that.
+
+    If you have a different format, please overwrite this function.
+    """
+    return utils.load(model_file)
+
+
+  def read_probe(self, probe_file):
+    """Reads the probe feature from file.
+    By default, the probe feature is identical to the projected feature.
+    Hence, this base class implementation simply calls self.read_feature(...).
+
+    If your tool requires different behavior, please overwrite this function.
+    """
+    return self.read_feature(probe_file)
+
+
+
+  def train_projector(self, training_features, projector_file):
+    """This function can be overwritten to train the feature projector.
+    If you do this, please also register the function by calling this base class constructor
+    and enabling the training by 'requires_projector_training = True'.
+
+    The training function gets two parameters:
+
+    - training_features: A list of *extracted* features that can be used for training the extractor.
+    - projector_file: The file to write. This file should be readable with the 'load_projector' function (see above).
+    """
+    raise NotImplementedError("Please overwrite this function in your derived class, or unset the 'requires_projector_training' option in the constructor.")
+
+
+  def load_projector(self, projector_file):
+    """Loads the parameters required for feature projection from file.
+    This function usually is only useful in combination with the 'train_projector' function (see above).
+    In this base class implementation, it does nothing.
+
+    Please register 'performs_projection = True' in the constructor to enable this function.
+    """
+    pass
+
+
+  def train_enroller(self, training_features, enroller_file):
+    """This function can be overwritten to train the model enroller.
+    If you do this, please also register the function by calling this base class constructor
+    and enabling the training by 'require_enroller_training = True'.
+
+    The training function gets two parameters:
+
+    - training_features: A dictionary of *extracted* or *projected* features, which are sorted by clients, that can be used for training the extractor.
+    - enroller_file: The file to write. This file should be readable with the 'load_enroller' function (see above).
+    """
+
+
+  def load_enroller(self, enroller_file):
+    """Loads the parameters required for model enrollment from file.
+    This function usually is only useful in combination with the 'train_enroller' function (see above).
+    This function is always called AFTER calling the 'load_projector'.
+    In this base class implementation, it does nothing.
+    """
+    pass