text:
""" Relative Margin Machines (original and some variants) """

# the output is a prediction vector
import timeit

from pySPACE.missions.nodes.decorators import NoOptimizationParameter, BooleanParameter, QNormalParameter, \
    ChoiceParameter, QLogUniformParameter, LogUniformParameter
from pySPACE.resources.data_types.prediction_vector import PredictionVector
from pySPACE.missions.nodes.classification.base import RegularizedClassifierBase
# classification vector may be saved as a feature vector
from pySPACE.resources.data_types.feature_vector import FeatureVector
from pySPACE.resources.data_types.time_series import TimeSeries
# timeout package
import signal

import numpy
from numpy import dot

from pySPACE.missions.nodes.classification.base import TimeoutException

import warnings
import logging

import random
import copy

from pySPACE.resources.dataset_defs.metric import BinaryClassificationDataset

from pySPACE.missions.nodes.classification.svm_variants.external import LibSVMClassifierNode

from pySPACE.missions.nodes.base_node import BaseNode

@NoOptimizationParameter("use_list")
@NoOptimizationParameter("omega")
@QLogUniformParameter("max_iterations", min_value=1, max_value=1000, q=1)
@LogUniformParameter("range_", min_value=1.01, max_value=1000)
@ChoiceParameter("offset_factor", choices=[0, 1, 10, 100])
@BooleanParameter("squared_loss")
@NoOptimizationParameter("linear_weighting")
class RMM2Node(RegularizedClassifierBase):
    """ Classify with 2-norm SVM relaxation (b in target function) for BRMM

    The balanced relative margin machine (BRMM) is a modification of the
    original relative margin machine (RMM).
    The details to this algorithm can be found in the given reference.

    This node extends a successive over relaxation algorithm
    for adaption on new data with some variants.

    For further details, have a look at the given reference,
    the *reduced_descent* method
    which is an elemental processing step and the *_inc_train*  method,
    which uses the status of the algorithm as a warm start.

    **References**

        ========= ==================================================
        main
        ========= ==================================================
        author    Krell, M. M. and Feess, D. and Straube, S.
        title     `Balanced Relative Margin Machine - The Missing Piece Between FDA and SVM Classification <http://dx.doi.org/10.1016/j.patrec.2013.09.018>`_
        journal   Pattern Recognition Letters
        publisher Elsevier
        doi       10.1016/j.patrec.2013.09.018
        year      2014
        ========= ==================================================

    **Parameters**

    Most parameters are already included into the
    :class:`RegularizedClassifierBase <pySPACE.missions.nodes.classification.base.RegularizedClassifierBase>`.

        :random:
            *Numerical recipes* suggests to randomize the order of alpha.
            *M&M* suggest to sort the alpha by their magnitude.

            (*optional, default: False*)

        :omega:
            Descent factor of optimization algorithm. Should be between 0 and 2!
            *Numerical recipes* uses 1.3 and *M&M* choose 1.0.

            (*optional, default: 1.0*)

        :version:
            Using the *matrix* with the scalar products or using only the
            *samples* and track changes in w and b for fast calculations.
            Both versions give totally the same result but they are available for
            comparison.
            Samples is mostly a bit faster.
            For  kernel usage only *matrix* is possible.

            (*optional, default: "samples"*)

        :reduce_non_zeros:
            In the inner loops, indices are rejected, if they loose there support.

            (*optional, default: True*)

        :linear_weighting:
            Add a linear weighting in the loss term.
            We use the index as weight and normalize it to have a total sum of 1.

            (*optional, default: False*)

        :calc_looCV:
            Calculate the leave-one-out metrics on the training data

            (*optional, default: False*)

        :range\_:
            Upper bound for the prediction value before its 'outer loss' is
            punished with `outer_complexity`.

            Using this parameter (with value >1) activates the RMM.

            (*optional, default: numpy.inf*)

        :outer_complexity:
            Cost factor for to high values in classification. (see `range\_`)

            (*optional, default: `complexity`*)

        :offset_factor:
            Reciprocal weight, for offset treatment in the model

                :0: Use no offset
                :1: Normal affine approach from augmented feature vectors
                :high: Only small punishment of offset, enabling larger offsets
                      (*danger of numerical instability*)

            If 0 is used, the offset b is set to zero, otherwise it is used via
            augmented feature vectors with different augmentation factors.
            The augmentation value corresponds to 1/*offset_factor*,
            where 1/0 corresponds to infinity.

            (*optional, default: 1*)

        :squared_loss:
            Use L2 loss (optional) instead of L1 loss (default).

            (*optional, default: False*)

    In the implementation we do not use alpha but dual_solution for the
    variables of the dual optimization problem,
    which is optimized with this algorithm.

    In the RMM case dual solution weights are concatenated.
    The RMM algorithm was constructed in the same way
    as in the mentioned references.

    As a stopping criterion we use the maximum change to be less than some
    tolerance.

    **Exemplary Call**

    .. code-block:: yaml

        -
            node : 2RMM
            parameters :
                complexity : 1.0
                weight : [1,3]
                debug : True
                store : True
                class_labels : ['Standard', 'Target']

    :input:    FeatureVector
    :output:   PredictionVector
    :Author:   Mario Michael Krell (mario.krell@dfki.de)
    :Created:  2012/06/27
    """
    def __init__(self, random=False, omega=1.0,
                 max_iterations=numpy.inf,
                 version="samples", reduce_non_zeros=True,
                 linear_weighting=False, calc_looCV=False,
                 range_=numpy.inf, outer_complexity=None,
                 offset_factor=1, squared_loss=False,
                 co_adaptive=False, co_adaptive_index=numpy.inf, history_index=1,
                 **kwargs):
        self.old_difference = numpy.inf
        # instead of lists, arrays are concatenated in training
        super(RMM2Node, self).__init__(use_list=False, **kwargs)

        if not(version in ["samples", "matrix"]):
            self._log("Version %s is not available. Default to 'samples'!"%version, level=logging.WARNING)
            version = "samples"
        if not self.kernel_type == 'LINEAR' and not version == "matrix":
            self._log(
                "Version %s is not available for nonlinear kernel. " % version +
                "Default to 'matrix'!", level=logging.WARNING)
            version = "matrix"
        range_ = numpy.float64(range_)
        if outer_complexity is None:
            outer_complexity = self.complexity
        # factor to only use relation between complexities
        # instead of outer_complexity
        complexity_correction = 1.0 * outer_complexity / self.complexity
        if self.tolerance > 0.1 * min(self.complexity, outer_complexity):
            self.set_permanent_attributes(tolerance=0.1*min(self.complexity,
                                                            outer_complexity))
            warnings.warn("Using too high tolerance. Reduced to 0.1 times " +
                          "complexity (tolerance=%f)." % self.tolerance)
        # mapping of the binary variables to {0,1}
        if not squared_loss:
            squ_factor = 0.0
        else:
            squ_factor = 1.0

        if version == "matrix":
            M = None
        else:
            M = []

        self.set_permanent_attributes(random=random,
                                      omega=omega,
                                      max_iterations_factor=max_iterations,
                                      max_sub_iterations=numpy.inf,
                                      iterations=0,
                                      sub_iterations=0,
                                      version=version,
                                      M=M,
                                      reduce_non_zeros=reduce_non_zeros,
                                      calc_looCV=calc_looCV,
                                      offset_factor=offset_factor,
                                      squ_factor=squ_factor,
                                      num_samples=0,
                                      outer_complexity=outer_complexity,
                                      complexity_correction=
                                      complexity_correction,
                                      linear_weighting=linear_weighting,
                                      range=range_,  # RBF for zero training
                                      b=0,
                                      w=None,
                                      bi=[],
                                      ci=[],
                                      dual_solution=None,
                                      one_class=False,
                                      co_adaptive=co_adaptive,
                                      co_adaptive_index=co_adaptive_index,
                                      history_index=history_index
                                      )

    def _execute(self, x):
        """ Executes the classifier on the given data vector in the linear case

        prediction value = <w,data>+b
        """
        if self.kernel_type == 'LINEAR':
            if self.w is None:
                self.w = numpy.zeros(x.shape[1], dtype=numpy.float)
            return super(RMM2Node, self)._execute(x)
        data = x.view(numpy.ndarray)
        data = data[0, :]
        prediction = self.b
        for i in range(self.num_samples):
            dual = self.dual_solution[i][0] - self.dual_solution[i][1]
            if not dual == 0:
                prediction += dual*self.kernel_func(data,
                                                    self.samples[i])*self.bi[i]
        # one-class multinomial handling of REST class
        if "REST" in self.classes and self.multinomial:
            if "REST" == self.classes[0]:
                label = self.classes[1]
            elif "REST" == self.classes[1]:
                label = self.classes[0]
                prediction *= -1
        # Look up class label
        # prediction --> {-1,1} --> {0,1} --> Labels
        elif prediction > 0:
            label = self.classes[1]
        else:
            label = self.classes[0]
        return PredictionVector(label=label, prediction=prediction,
                                predictor=self)

    def _stop_training(self, debug=False):
        """ Train the SVM with the SOR algorithm on the collected training data
        """
        self._log("Preprocessing of SOR SVM")
        self._log("Instances of Class %s: %s, %s: %s"
                  % (self.classes[0],
                     self.labels.count(self.classes.index(self.classes[0])),
                     self.classes[1],
                     self.labels.count(self.classes.index(self.classes[1]))))
        ## initializations of relevant values and objects ##
        self.calculate_weigts_and_class_factors()
        self.num_samples = len(self.samples)
        self.max_iterations = self.max_iterations_factor*self.num_samples
        # RMM variable
        self.dual_solution = numpy.zeros((self.num_samples, 2))

        if self.version == "matrix" and self.kernel_type == "LINEAR":
            self.A = numpy.array(self.samples)
            self.D = numpy.diag(self.bi)
            self.M = dot(self.D,
                         dot(dot(self.A, self.A.T) + self.offset_factor *
                             numpy.ones((self.num_samples, self.num_samples)),
                             self.D))
        elif self.version == "samples" and self.kernel_type == "LINEAR":
            if not self.squ_factor:
                self.M = \
                    [1.0/(numpy.linalg.norm(sample)**2.0 + self.offset_factor)
                     for sample in self.samples]
            else:
                self.M = [(1 / (numpy.linalg.norm(self.samples[i])**2.0
                          + self.offset_factor + 1 / (2 * self.ci[i])),
                          1 / (numpy.linalg.norm(self.samples[i])**2.0
                          + self.offset_factor + 1 /
                          (2 * self.ci[i] * self.complexity_correction)))
                          for i in range(self.num_samples)]
            # changes of w and b are tracked in the samples version
            self.w = numpy.zeros(self.dim, dtype=numpy.float)
        else:
            ## iterative calculation of M
            self.M = numpy.zeros((self.num_samples, self.num_samples))
            for i in range(self.num_samples):
                bi = self.bi[i]
                si = self.samples[i]
                for j in range(self.num_samples):
                    if i > j:
                        self.M[i][j] = self.M[j][i]
                    else:
                        self.M[i][j] = bi * self.bi[j] * (
                            self.kernel_func(si, self.samples[j])
                            + self.offset_factor)

        ## SOR Algorithm ##
        self.iteration_loop(self.M)

        self.classifier_information["~~Solver_Iterations~~"] = self.iterations
        try:
            self.classifier_information["~~offset~~"] = self.b
            self.classifier_information["~~w0~~"] = self.w[0]
            self.classifier_information["~~w1~~"] = self.w[1]
        except:
            pass
        ## calculate leave one out metrics ##
        if self.calc_looCV:
            self.looCV()

    def looCV(self):
        """ Calculate leave one out metrics """
        # remember original solution
        optimal_w = copy.deepcopy(self.w)
        optimal_b = copy.deepcopy(self.b)
        optimal_dual_solution = copy.deepcopy(self.dual_solution)
        # preparation of sorting
        sort_dual = self.dual_solution[:, 0] + self.dual_solution[:, 1]
        # sort indices --> zero weights do not need any adaption and
        # low weights are less relevant for changes
        sorted_indices = map(list,[numpy.argsort(sort_dual)])[0]
        sorted_indices.reverse()

        prediction_vectors = []
        using_initial_solution = True
        for index in sorted_indices:
            d_i = self.dual_solution[index, 0]-self.dual_solution[index, 1]
            # delete each index from the current observation
            if d_i == 0 and using_initial_solution:
                # no change in classifier necessary
                pass
            else:
                # set weight to zero and track the corresponding changes
                self.reduce_dual_weight(index)
                # reiterate till convergence but skip current index
                temp_iter = self.iterations
                self.iteration_loop(self.M, reduced_indices=[index])
                self.iterations += temp_iter
                using_initial_solution = False
            prediction_vectors.append((self._execute(
                numpy.atleast_2d(self.samples[index])),
                self.classes[self.labels[index]]))
        self.loo_metrics = BinaryClassificationDataset.calculate_metrics(
            prediction_vectors,
            ir_class=self.classes[1],
            sec_class=self.classes[0])
        #undo changes
        self.b = optimal_b
        self.w = optimal_w
        self.dual_solution = optimal_dual_solution

    def reduce_dual_weight(self,index):
        """ Change weight at index to zero """
        if self.version == "sample":
            old_weight = self.dual_solution[index]
            old_weight = old_weight[0] - old_weight[1]
            self.update_classification_function(delta=-old_weight, index=index)
        else:
            # the matrix algorithm doesn't care for the old weights
            pass
        self.dual_solution[index] = [0, 0]

    def calculate_weigts_and_class_factors(self):
        """ Calculate weights in the loss term and map label to -1 and 1 """
        # Weights for soft margin (dependent on class or time)
        self.ci = []
        # Mapping from class to value of classifier (-1,1)
        self.bi = []
        self.num_samples = len(self.samples)
        for label in self.labels:
            self.append_weights_and_class_factors(label)
            #care for zero sum
        if self.linear_weighting:
            c = 1/(self.num_samples*(self.num_samples+1)*0.5)
            self.ci = [c*value for value in self.ci]

    def append_weights_and_class_factors(self, label):
        """ Mapping between labels and weights/class factors

        The values are added to the corresponding list.
        This is done in a separate function, since it is also needed for adaption.
        """
        self.bi.append(label*2-1)
        if label == 0:
            if self.linear_weighting:
                self.ci.append(self.complexity*self.weight[0]*self.num_samples)
            else:
                self.ci.append(self.complexity*self.weight[0])
        else:
            if self.linear_weighting:
                self.ci.append(self.complexity*self.weight[1]*self.num_samples)
            else:
                self.ci.append(self.complexity*self.weight[1])

    def iteration_loop(self, M, reduced_indices=[]):
        """ The algorithm is calling the :func:`reduced_descent<pySPACE.missions.nodes.classifiers.ada_SVM.SORSVMNode.reduced_descent>` method in loops over alpha

        In the first step it uses a complete loop over all components of alpha
        and in the second inner loop only the non zero alpha are observed till
        come convergence criterion is reached.

        *reduced_indices* will be skipped in observation.
        """
        ## Definition of tracking variables ##
        self.max_iterations = self.max_iterations_factor*self.num_samples
        self.iterations = 0
        self.difference = numpy.inf

        # recalculate factor in case of changing complexities
        self.complexity_correction = \
            1.0 * self.outer_complexity / self.complexity
        # initial call on all samples
        self.total_descent(self.dual_solution, M, reduced_indices)
        ## outer iteration loop ##
        while self.difference > self.tolerance and \
                self.iterations < self.max_iterations:
            # inner iteration loop only on active vectors/alpha (non zero) ##
            self.sub_iterations = 0
            # sorting or randomizing non zero indices
            # arrays are mapped to lists for later iteration
            sort_dual = self.dual_solution[:, 0] + self.dual_solution[:, 1]

            num_non_zeros = len(map(list, sort_dual.nonzero())[0])
            max_values = len(map(list,
                                 numpy.where(sort_dual == sort_dual.max()))[0])
            # sort the entries of the current dual
            # and get the corresponding indices
            sorted_indices = map(list, [numpy.argsort(sort_dual)])[0]
            if num_non_zeros == 0 or num_non_zeros == max_values:
                # skip sub iteration if everything is zero or maximal
                active_indices = []
            else:
                active_indices = sorted_indices[-num_non_zeros:-max_values]
            for index in reduced_indices:
                try:
                    active_indices.remove(index)
                except ValueError:
                    pass
            if self.random:
                random.shuffle(active_indices)
                #min(self.max_iterations_factor, 200) * \
            self.max_sub_iterations = self.max_iterations_factor * \
                len(active_indices) * 0.5
            while (self.difference > self.tolerance and
                   self.sub_iterations < self.max_sub_iterations
                   and self.iterations < self.max_iterations):
                ## iteration step ##
                self.reduced_descent(self.dual_solution, M,
                                         active_indices)
                ## outer loop ##
            if not (self.iterations < self.max_iterations):
                break
            # For the first run, the previous reduced descent is skipped
            # but for adaptivity and looCV it is important
            # to have first the small loop, since normally, this is sufficient.
            # Furthermore having it at the end simplifies the stop criterion
            self.max_sub_iterations = numpy.inf
            self.total_descent(self.dual_solution, M, reduced_indices)
            ## Final solution ##
        # in the case without kernels, we have to calculate the result
        # by hand new for each incoming sample
        if self.version == "matrix":
            dual_diff = self.dual_solution[:, 0] - self.dual_solution[:, 1]
            if self.offset_factor:  # else: keep b fixed and do NOT change
                self.b = self.offset_factor * dot(dual_diff, self.bi)
            if self.kernel_type == "LINEAR":
                self.w = numpy.array([dot(dot(self.A.T, self.D),
                                          dual_diff)]).T
        elif self.version == "samples" and self.kernel_type == "LINEAR":
            # w and b are pre-computed in the loop
            # transferring of 1-d array to 2d array
            # self.w = numpy.array([self.w]).T
            pass

    def reduced_descent(self,current_dual, M, relevant_indices):
        """ Basic iteration step over a set of indices, possibly subset of all

        The main principle is to make a descent step with just one index,
        while fixing the other dual_solutions.

        The main formula comes from *M&M*:

        .. math::

            d        = \\alpha_i - \\frac{\\omega}{M[i][i]}(M[i]\\alpha-1)

            \\text{with } M[i][j]  = y_i y_j(<x_i,x_j>+1)

            \\text{and final projection: }\\alpha_i = \\max(0,\\min(d,c_i)).

        Here we use c for the weights for each sample in the loss term,
        which is normally complexity times corresponding class weight.
        y is used for the labels, which have to be 1 or -1.

        In the *sample* version only the diagonal of M is used.
        The sum with the alpha is tracked by using the classification vector w
        and the offset b.

        .. math::

            o        = \\alpha_i

            d        = \\alpha_i - \\frac{\\omega}{M[i][i]}(y_i(<w,x_i>+b)-1)

            \\text{with projection: }\\alpha_i = \\max(0,\\min(d,c_i)),

            b=b+(\\alpha_i-o)y_i

            w=w+(\\alpha_i-o)y_i x_i
        """
        self.irrelevant_indices = []
        self.difference = 0
        if self.version == "matrix":
            dual_diff = current_dual[:, 0] - current_dual[:, 1]
        for i in relevant_indices:
            if not (self.sub_iterations < self.max_sub_iterations
                    and self.iterations < self.max_iterations):
                break
            beta = False
            old_dual = current_dual[i]
            ### Main Function ###
            ### elemental update step of SOR algorithm ###
            dual_1 = current_dual[i,0]
            dual_2 = current_dual[i,1]
            if self.version == "samples":
                xi = self.samples[i]
                bi = self.bi[i]
                fi = bi * (dot(xi.T, self.w) + self.b)
            elif self.version == "matrix":
                fi = dot(M[i], dual_diff)
                if self.one_class:
                    # correction if offset should not be changed
                    fi += self.bi[i] * self.b
            s1 = self.squ_factor / (2 * self.ci[i]) * dual_1
            s2 = self.squ_factor / (2 * self.ci[i] *
                                    self.complexity_correction) * dual_2
            if dual_1 > 0:  # alpha update
                old_dual = dual_1
                if self.version == "matrix":
                    x = old_dual - \
                        self.omega / (M[i][i]
                                      + self.squ_factor / (2 * self.ci[i])) \
                        * (fi - 1 + s1)
                elif self.version == "samples" and self.squ_factor:
                    x = old_dual - self.omega * M[i][0] * (fi - 1 + s1)
                elif self.version == "samples" and not self.squ_factor:
                    x = old_dual - self.omega * M[i] * (fi - 1)
            elif dual_2 > 0:  # beta update
                old_dual = dual_2
                if self.version == "matrix":
                    x = old_dual - \
                        self.omega / (M[i][i] + self.squ_factor /
                        (2 * self.ci[i] * self.complexity_correction)) \
                        * (self.range - fi + s2)
                elif self.version == "samples" and self.squ_factor:
                    x = old_dual - self.omega * M[i][1] \
                        * (self.range - fi + s2)
                elif self.version == "samples" and not self.squ_factor:
                    x = old_dual - self.omega * M[i] \
                        * (self.range - fi)
                beta = True
            else:
                # both values are zero and we need to check,
                #if one gets active alpha or beta dual coefficient
                old_dual = 0 # just used to have the same formulas as above
                if self.version == "matrix":
                    x = old_dual - \
                        self.omega/(M[i][i]
                                    + self.squ_factor / (2 * self.ci[i])) \
                        * (fi - 1 + s1)
                elif self.version == "samples" and self.squ_factor:
                    x = old_dual - self.omega * M[i][0] * (fi - 1 + s1)
                elif self.version == "samples" and not self.squ_factor:
                    x = old_dual - self.omega * M[i] * (fi - 1)
                # no update of alpha but update of beta (eventually zero update)
                if not x > 0:
                    beta = True
                    if self.version == "matrix":
                        x = old_dual - \
                            self.omega / (M[i][i] + self.squ_factor /
                            (2 * self.ci[i] * self.complexity_correction)) \
                            * (self.range - fi + s2)
                    elif self.version == "samples" and self.squ_factor:
                        x = old_dual - self.omega * M[i][1] * (
                            self.range - fi + s2)
                    elif self.version == "samples" and not self.squ_factor:
                        x = old_dual - self.omega * M[i] * (
                            self.range - fi + s2)
            # map dual solution to the interval [0,C] in L1 case or
            # just make it positive in the L2 case
            # current_dual[i]=self.project(x,index=i)
            if x <= 0:
                self.irrelevant_indices.append(i)
                current_dual[i] = [0, 0]
            elif not beta and not self.squ_factor:
                current_dual[i, 0] = min(x, self.ci[i])
            elif beta and not self.squ_factor:
                current_dual[i, 1] = min(x, self.ci[i] *
                                         self.complexity_correction)
            elif not beta and self.squ_factor:
                current_dual[i, 0] = x
            elif beta and self.squ_factor:
                current_dual[i, 1] = x
            if self.version == "matrix":
                old_diff = dual_diff[i]
                dual_diff[i] = current_dual[i, 0] - current_dual[i, 1]
                delta = dual_diff[i] - old_diff
                # update w and b in samples case
            if self.version == "samples":
                delta = (current_dual[i, 0] + current_dual[i, 1]
                         - old_dual) * bi
                # for beta:  difference needed
                if beta:
                    delta = -delta
                # update classification function parameter w and b
                # self.update_classification_function(delta=delta, index=i)
                self.b += self.offset_factor * delta
                self.w += delta * xi
            current_difference = numpy.abs(delta)
            # if current_difference > self.difference:
            #     self.difference = current_difference
            self.difference += current_difference
            self.sub_iterations += 1
            self.iterations += 1
        if self.reduce_non_zeros:
            for index in self.irrelevant_indices:
                relevant_indices.remove(index)
        if self.random:
            random.shuffle(relevant_indices)

    def update_classification_function(self, delta, index):
        """ update classification function parameter w and b """
        bi=self.bi[index]
        self.b = self.b + self.offset_factor * delta * bi
        self.w = self.w + delta * bi * self.samples[index]

    def project(self,value,index):
        """ Projection method of *soft_relax* """
        if value<=0:
            self.irrelevant_indices.append(index)
            return 0
        else:
            return min(value, self.ci[index])

    def total_descent(self,current_dual,M,reduced_indices=[]):
        """ Different sorting of indices and iteration over all indices

        .. todo:: check, which parameters are necessary
        """
        if not self.random:
            sorted_indices = range(self.num_samples)
            # if current_dual.ndim == 2:
            #     sort_dual = current_dual[:,0]+current_dual[:,1]
            # else:
            #     sort_dual = current_dual
            # # highest first
            # sort_dual = sort_dual * -1.0
            # # sort the entries of the current dual and get the corresponding indices
            # sorted_indices = map(list,[numpy.argsort(sort_dual)])[0]
            # #sorted_indices.reverse()
        else:
            sorted_indices = range(self.num_samples)
            random.shuffle(sorted_indices)
        for index in reduced_indices:
            sorted_indices.remove(index)
        self.reduced_descent(current_dual, M,sorted_indices)

    def _inc_train(self,data,label):
        """ Warm Start Implementation by Mario Michael Krell

        The saved status of the algorithm, including the Matrix M, is used
        as a starting point for the iteration.
        Only the problem has to be lifted up one dimension.
        """
        #one vs. REST case
        if "REST" in self.classes and not label in self.classes:
            label = "REST"
        # one vs. one case
        if not self.multinomial and len(self.classes) == 2 and \
                not label in self.classes:
            return
        self._train(data, label)
        # here it is important to use the mapped label
        self.append_weights_and_class_factors(self.labels[-1])
        if self.linear_weighting:
            self.ci[-1] = self.ci[-1] * 2 / (
                self.num_samples * (self.num_samples + 1))
            scaling = self.num_samples/(self.num_samples+2)
            self.ci = [value * scaling for value in self.ci]
            self.b *= scaling
            self.w *= scaling

        self.num_samples += 1

        # The new example is at first assumed to be irrelevant (zero weight).
        if self.dual_solution is None:
            self.dual_solution = numpy.zeros((1,2))
        else:
            self.dual_solution = numpy.append(self.dual_solution, [[0.0, 0.0]],
                                              axis=0)
        # update of the relevant matrix
        if self.version == "matrix":
            # very inefficient!!!
            M = self.M
            self.M = numpy.zeros((self.num_samples,self.num_samples))
            if not M is None:
                self.M[:-1, :-1] = M
            del M
            bj = self.bi[-1]
            d = self.samples[-1]
            # calculation of missing entries of matrix M by hand
            for i in range(self.num_samples):
                if self.kernel_type == "LINEAR":
                    # y_i*y_j*(<x_i,x_j>+1)
                    self.M[-1, i] = bj*self.bi[i]*(
                        self.kernel_func(d, self.samples[i])+self.offset_factor)
                    self.M[i, -1] = self.M[-1, i]
                else:
                    raise NotImplementedError
        elif self.version == "samples":
            # very efficient :)
            if not self.squ_factor:
                self.M.append(1.0/(numpy.linalg.norm(self.samples[-1])**2.0
                                   + self.offset_factor))
            else:
                self.M.append(
                    (1 / (numpy.linalg.norm(self.samples[-1])**2.0
                          + self.offset_factor + 1 / (2 * self.ci[-1])),
                     1 / (numpy.linalg.norm(self.samples[-1])**2.0
                          + self.offset_factor + 1 /
                          (2 * self.ci[-1] * self.complexity_correction))
                    ))
        prediction = self._execute(data)
        if (not prediction.label == label or abs(prediction.prediction) < 1 or
                (self.range and abs(prediction.prediction) > self.range)):
            if self.co_adaptive:
                self.update_data()
            if self.version == "matrix":
                # relevant parameters for getting w and b
                # updates should be done using old variables
                self.A = numpy.array(self.samples)
                self.D = numpy.diag(self.bi)
            temp_iter = self.iterations
            self.iteration_loop(self.M)
            self.iterations += temp_iter

    def reiterate(self, parameters):
        """ Change the parameters in the model and reiterate """
        changed_c = False
        for parameter in parameters:
            p = parameter.strip("~")
            if p.startswith("log"):
                value = 10 ** parameters[parameter]
                p = p[4:]
            else:
                value = parameters[parameter]
            if p in ["range", "R", "Range", "range_"]:
                self.range = value
            elif p in ["complexity","C", "Complexity"] \
                    and not value == self.complexity:
                changed_c = True
                difference = abs(self.complexity-value)
                self.complexity = value
                if self.tolerance > 0.1 * difference:
                    self.set_permanent_attributes(tolerance=0.1*difference)
                # # fix ci
                # scaling = 1.0 * self.complexity / old_complexity
                # self.ci = [value * scaling for value in self.ci]
            elif p in ["w1", "W1", "weight1", "Weight1"] \
                    and not value == self.weight[0]:
                changed_c = True
                difference = abs(self.weight[0]-value)
                if self.tolerance > 0.1 * difference:
                    self.set_permanent_attributes(tolerance=0.1*difference)
                self.weight[0] = value
            elif p in ["w2", "W2", "weight2", "Weight2"] \
                    and not value == self.weight[1]:
                changed_c = True
                difference = abs(self.weight[1]-value)
                if self.tolerance > 0.1 * difference:
                    self.set_permanent_attributes(tolerance=0.1*difference)
                self.weight[1] = value
        # recalculate ci
        if changed_c:
            self.calculate_weigts_and_class_factors()
        # fix M
        if self.version == "samples" and self.kernel_type == "LINEAR" \
                and self.squ_factor and changed_c:
            self.M = [(1 / (numpy.linalg.norm(self.samples[i])**2.0
                      + self.offset_factor + 1 / (2 * self.ci[i])),
                      1 / (numpy.linalg.norm(self.samples[i])**2.0
                      + self.offset_factor + 1 /
                      (2 * self.ci[i] * self.complexity_correction)))
                      for i in range(self.num_samples)]
        self.iteration_loop(self.M)

    def append_sample(self, sample):
        super(RMM2Node, self).append_sample(sample)
        if self.co_adaptive:
            try:
                hist_data = copy.deepcopy(sample.history[self.history_index-1])
            except IndexError:
                self._log("No history data available for classifier! " +
                          "Co-adaptivity is turned off.",
                          level=logging.CRITICAL)
                self.co_adaptive = False
            if not "hist_samples" in self.__dict__:
                self.hist_samples = []
            self.hist_samples.append(hist_data)

    def update_data(self):
        """ Recalculate w, samples and M """
        # initialize w
        self.w = numpy.zeros(self.dim, dtype=numpy.float)
        for i in range(self.num_samples):
            # recalculate sample
            new_sample = self.get_previous_execute(
                data=self.hist_samples[i], number=self.co_adaptive_index)
            data_array = new_sample.view(numpy.ndarray)
            self.samples[i] = data_array[0, :]
            # recalculate w
            self.w += data_array[0, :] * self.bi[i] * \
                (self.dual_solution[i][0] - self.dual_solution[i][1])
        if not self.squ_factor:
            self.M = \
                [1.0/(numpy.linalg.norm(sample)**2.0 + self.offset_factor)
                 for sample in self.samples]
        else:
            self.M = [(1 / (numpy.linalg.norm(self.samples[i])**2.0
                      + self.offset_factor + 1 / (2 * self.ci[i])),
                      1 / (numpy.linalg.norm(self.samples[i])**2.0
                      + self.offset_factor + 1 /
                      (2 * self.ci[i] * self.complexity_correction)))
                      for i in range(self.num_samples)]

# ===========================================================================
@BooleanParameter("normalize_C")
@QNormalParameter("range", mu=2, sigma=1, q=1)
class RMM1ClassifierNode(RegularizedClassifierBase):
    """ Classify with 1-Norm SVM and relative margin

    Implementation via Simplex Algorithms for exact solutions.

    It is important, that the data is reduced
    and has not more then 2000 features.

    This algorithm is an extension of the original RMM as outlined in the
    reference.

    **References**

        ========= ==================================================
        main
        ========= ==================================================
        author    Krell, M. M. and Feess, D. and Straube, S.
        title     `Balanced Relative Margin Machine - The Missing Piece Between FDA and SVM Classification <http://dx.doi.org/10.1016/j.patrec.2013.09.018>`_
        journal   Pattern Recognition Letters
        publisher Elsevier
        doi       10.1016/j.patrec.2013.09.018
        year      2013
        ========= ==================================================

    **Parameters**
        :complexity:
            Complexity sets the weighting of punishment for misclassification
            in comparison to generalizing classification from the data.
            Value in the range form 0 to infinity.

            (*optional, default: 1*)

        :outer_complexity:
            Outer complexity sets the weighting of punishment being outside
            the *range*
            in comparison to generalizing classification from the data
            and the misclassification above.
            Value in the range form 0 to infinity.
            By default it uses the outer complexity.
            For using infinity, use numpy.inf, a string containing *inf* or
            a negative value.

            (*recommended, default: complexity_value*)

        :weight:
            Defines parameter for class weights.
            I is an array with two entries.
            Set the parameter C of class i to weight*C.

            (*optional, default: [1,1]*)

        :range:
            Defines constraint radius for the outer boarder of the classification.
            Going to infinity, this classifier will be identical to the 1 Norm
            SVM. This parameter should be always greater then one.
            Going to one, the classifier will be a variant of the
            Regularized Linear Discriminant analysis.

            (*optional, default: 2*)

        :class_labels:
            Sets the labels of the classes.
            This can be done automatically, but setting it will be better,
            if you want to have similar predictions values
            for classifiers trained on different sets.
            Otherwise this variable is built up by occurrence of labels.
            Furthermore the important class (ir_class) should get the
            second position in the list, such that it gets higher
            prediction values by the classifier.

            (*recommended, default: []*)

        :debug:
            If *debug* is True one gets additional output
            concerning the classification.

            (*optional, default: False*)

        :store:
            Parameter of super-class. If *store* is True,
            the classification vector is stored as a feature vector.

            (*optional, default: False*)

    **Exemplary Call**

    .. code-block:: yaml

        -
            node : 1RMM
            parameters :
                complexity : 1.0
                weight : [1,2.5]
                debug : False
                store : True
                range : 2

    :input:    FeatureVector
    :output:   PredictionVector
    :Author: Mario Krell (Mario.krell@dfki.de)
    :Revised: 2010/04/09

    """
    def __init__(self, normalize_C=False, outer_complexity=None, range=2,
                 **kwargs):
        super(RMM1ClassifierNode, self).__init__(**kwargs)

        # Check if old parameter name 'sensitivity' was used instead of range
        if 'sensitivity' in kwargs:
            range = kwargs['sensitivity']
            self._log("Please use parameter 'range' instead of 'sensitivity'" +
                      " in 1RMM Node!", level=logging.ERROR)

        if outer_complexity is None:
            self._log("No outer complexity given, set to complexity parameter!",
                      level=logging.WARNING)
            outer_complexity = self.complexity
        elif outer_complexity < 0 or outer_complexity == numpy.inf or \
                (type(outer_complexity) == str and "inf" in outer_complexity):
            outer_complexity = None

        self.set_permanent_attributes(outer_complexity=outer_complexity,
                                      normalize_C=normalize_C,
                                      range=range)

    # Here we define a function that defines the matrix, so that we can
    # insert it as an argument for the solver and optimize it
    def create_problem_matrix(self, n, e):
        # First, the declaration of variables

        # We only need to define within the function those variables which we
        # don't need in the _stop_training() function
        ci = []
        co = []
        bi = numpy.array(self.bi)
        for label in self.labels:
            if label == 0:
                if not self.normalize_C:
                    ci.append(1.0/(1.0*self.complexity*self.weight[0]))
                    co.append(1.0/(1.0*self.outer_complexity*self.weight[0]))
                else:
                    ci.append(1.0/(1.0*self.complexity*self.weight[0]/n))
                    co.append(1.0/(1.0*self.outer_complexity*self.weight[0]/n))
            else:
                if not self.normalize_C:
                    ci.append(1.0/(1.0*self.complexity*self.weight[1]))
                    co.append(1.0/(1.0*self.outer_complexity*self.weight[1]))
                else:
                    ci.append(1.0/(1.0*self.complexity*self.weight[1]/n))
                    co.append(1.0/(1.0*self.outer_complexity*self.weight[1]/n))

        if not self.outer_soft_margin:
            co = n*[0]

        # Now we start defining the matrix by chunks, as follows
        #
        #
        #
        #         dim        dim     1       n
        #      *********  *********  *  d**-C*...***
        #      *********  *********  *  *i*-I*...***
        #   n  ***(-D)**  ***(D)***  B     .
        #      *********  *********  I       .
        #      *********  *********  *  ****...**a**
        #      *********  *********  *  ****...***g*
        #
        #         dim        dim     1       n
        #      *********  *********  *  d**-C*...***
        #      *********  *********  *  *i*-O*...***
        #   n  ***(D)***  ***(-D)**  B     .
        #      *********  *********  I       .
        #      *********  *********  *  ****...**a**
        #      *********  *********  *  ****...***g*
        #
        #      -1**************...******************
        #      *-1*************...******************
        #      **-1************...******************
        #          .
        #             .
        # 2dim            .
        #  +n                 .
        #      ****************-1***+****...********
        #      *****************-1**+****...********
        #      ******************-1*+****...********
        #      *******************-1+****...********
        #      ********************+-1***...********
        #      ********************+*-1**...********
        #      ********************+**-1*...********
        #                                .
        #                                   .
        #      ********************+****...******-1*
        #      ********************+****...*******-1
        #
        # Note : there is a skipped vertical row, marked by the "+",
        # at 2*dim + 1 in the matrix.

        output_matrix = numpy.zeros((2*self.dim+3*n,2*self.dim+n+1), 'd')

        output_matrix[:n, :self.dim] = \
            - numpy.multiply(numpy.array(self.samples).T, bi).T
        output_matrix[n:2*n, :self.dim] = - output_matrix[:n, :self.dim]
        output_matrix[:n, self.dim:2*self.dim] = output_matrix[n:2*n, :self.dim]
        output_matrix[n:2*n, self.dim:2*self.dim] = output_matrix[:n, :self.dim]
        output_matrix[:n, 2*self.dim] = - numpy.array(self.bi)
        output_matrix[n:2*n, 2*self.dim] = output_matrix[:n, 2*self.dim]
        output_matrix[:n, 2*self.dim+1:2*self.dim+n+1] = - numpy.diag(ci)
        output_matrix[n:2*n, 2*self.dim+1:2*self.dim+n+1] = - numpy.diag(co)
        output_matrix[2*n:2*self.dim+2*n, :2*self.dim] = - numpy.diag(e+e)
        output_matrix[2*self.dim+2*n:, 2*self.dim+1:] = - numpy.diag(n*[1.0])

        return output_matrix

    def _stop_training(self, debug = False):
        """ Finish the training, i.e. train the SVM.

        This makes the same as the 1-Norm RMM,
        except that there are additional restrictions pushing the classification
        into two closed intervals instead of two open.
        At both ends there is the same kind of soft Margin."""
        self._log("Preprocessing of 1-Norm RMM")
        self._log("Instances of Class %s: %s, %s: %s"
                  % (self.classes[0],
                     self.labels.count(self.classes.index(self.classes[0])),
                     self.classes[1],
                     self.labels.count(self.classes.index(self.classes[1]))))
        # Dimension of the data
        self.num_samples = len(self.samples)
        n = self.num_samples
        e = self.dim*[1.0]
        if self.outer_complexity is None:
            self.outer_soft_margin = False
            self.outer_complexity = self.complexity  # arbitrary value
        else:
            self.outer_soft_margin = True
        # Mapping from class to value of classifier
        self.bi = []
        for label in self.labels:
            #if self.classes.index(label) == 0:
            if label == 0:
                self.bi.append(-1)
            else:
                self.bi.append(1)

        # Import the necessary optimization module.
        try:
            import cvxopt
            import cvxopt.solvers
        except ImportError:
            raise Exception("Using the 1-Norm-SVM requires the Python CVXOPT module.")

        self._log("optimization preparation")
        # Target function (w+ + w- + \sum t_i)
        # Weighting is done in the inequalities
        c = cvxopt.matrix(e+e+[0]+n*[1.0])

        # First the n classification restrictions
        # then  the n restrictions for the outer margin
        # and finally the restrictions for positive variables
        h = numpy.hstack((-numpy.ones(n,'d'), self.range*numpy.ones(n,'d'),
                          numpy.zeros(2*self.dim+n)))
        h = cvxopt.matrix(h)

        #Suppress printing of GLPK and cvxopt
        if not self.debug:
            cvxopt.solvers.options['LPX_K_MSGLEV'] = 0
            cvxopt.solvers.options['show_progress'] = self.debug
            # try:
            #     import mosek
            #     cvxopt.solvers.options['MOSEK'] = {mosek.iparam.log: 0}
            # except:
            #     warnings.warn('No Mosek import possible!')

        # Do the optimization
        # (-c_i x_i,c_i x_i, -c_i,-e_i) * (w+,w-,b,t) \leq -1
        # (c_i x_i,-c_i x_i, c_i,-e_i) * (w+,w-,b,t) \leq R
        # (-w+,-w-,-t) \leq 0
        self._log("Classifier under construction")

        if not self.max_time is None:
            cvxopt.solvers.options['LPX_K_TMLIM'] = int(self.max_time)

        self.sol = cvxopt.solvers.lp(
            c,
            cvxopt.matrix(self.create_problem_matrix(n, e)), h, solver='glpk')

        self._log("Construction complete")

        model = []
        self.calculate_classification_vector(model)
        if self.debug:
            self.calculate_slack_variables(model)

            inner_dual_sol = self.sol['z'][:n]
            dual_inner_weights = numpy.multiply(numpy.array(inner_dual_sol).T,
                numpy.array(self.bi))[0]
            self.max_inner_weight = []
            self.max_inner_weight.append(-dual_inner_weights.min())
            self.max_inner_weight.append(dual_inner_weights.max())
            if self.debug:
                print "Maximal used inner weights, depending on class:"
                print self.max_inner_weight
            outer_dual_sol = self.sol['z'][n:2*n]
            dual_outer_weights = numpy.multiply(numpy.array(outer_dual_sol).T,
                numpy.array(self.bi))[0]
            self.max_outer_weight = []
            self.max_outer_weight.append(-dual_outer_weights.min())
            self.max_outer_weight.append(dual_outer_weights.max())
            if self.debug:
                print "Maximal used inner weights, depending on class:"
                print self.max_outer_weight
            self.max_weight = []
            self.max_weight.append(max(self.max_outer_weight[0],self.max_inner_weight[0]))
            self.max_weight.append(max(self.max_outer_weight[1],self.max_inner_weight[1]))
            if self.debug:
                print "Maximal used weights, depending on class:"
                print self.max_weight
            print "RMM 1 parameter results:"
            self.print_variables()
            print str(self.outer_margin), " vectors of ", \
                str(self.num_samples), \
                " have been used for the outer margin and "
            numpy.set_printoptions(edgeitems=100, linewidth=75, precision=2,
                                   suppress=True, threshold=1000)
            print self.to, " are the outer Slack variables."
            numpy.set_printoptions(edgeitems=3, infstr='Inf', linewidth=75,
                                   nanstr='NaN', precision=8, suppress=False,
                                   threshold=1000)
            print "The number of support vectors(", self.num_sv, \
                ") can be split into", self.num_osv, " and ", self.num_isv, \
                "for the outer and inner margin support vectors."

        self.delete_training_data()

    # algorithms to stop process after a certain time
    # from http://pguides.net/python-tutorial/python-timeout-a-function/
    # Mainly Code copy from 1SVM
    def get_solution_with_timeout(self,c,n,e,h):
        def timeout_handler(signum, frame):
            raise TimeoutException()

        # Import the necessary optimization module.
        try:
            import cvxopt
            import cvxopt.solvers
        except ImportError:
            raise Exception("Using the 1-Norm-RMM requires the Python CVXOPT module.")

        old_handler = signal.signal(signal.SIGALRM, timeout_handler)
        signal.alarm(self.max_time) # trigger alarm in self.time seconds

        try:
            self.sol = cvxopt.solvers.lp(
                c, cvxopt.matrix(self.create_problem_matrix(n,e)),
                                 h, solver='glpk')
        except TimeoutException:
            self._log("Classifier construction interrupted!", level=logging.CRITICAL)
            self.sol = None
            return
        finally:
            signal.signal(signal.SIGALRM, old_handler)

        signal.alarm(0)
        self._log("Finished classifier construction successfully.")
        return

    def calculate_slack_variables(self,model):
        """ Calculate slack variables from the given SVM model """
        self.t=self.sol['x'][2*self.dim+1:]
        # differentiation between inner and outer soft margin vectors
        self.num_sv = 0
        self.num_osv = 0
        self.num_isv = 0
        # count the margin vectors lying not at the border of the margin
        # but inside
        self.inner_margin = 0
        self.outer_margin = 0
        self.ti = []
        self.to = []
        for i in range(self.num_samples):
            p = 2*(self.labels[i]-0.5)*((numpy.dot(self.w.T,
                                                   self.samples[i]))[0]+self.b)
            if p >= self.range:
                self.ti.append(0)
                self.num_sv += 1
                self.num_osv += 1
                if p < 1e-5+self.range:
                    self.to.append(0)
                else:
                    self.to.append(p-self.range)
                    self.outer_margin += 1
            elif p > 1:
                self.ti.append(0)
                self.to.append(0)
            else:
                self.to.append(0)
                self.num_sv += 1
                self.num_isv += 1
                if (1-p) < 1e-9:
                    self.ti.append(0)
                else:
                    self.ti.append(1-p)
                    self.inner_margin += 1

    def calculate_classification_vector(self, model):
        """ Copy from Norm1ClassifierNode due to avoid cross importing """
        w = numpy.zeros((self.dim,1))
        self.num_retained_features = 0
        try:
            self.b = float(self.sol['x'][2*self.dim])
            self.w = numpy.array(self.sol['x'][:self.dim]
                                 - self.sol['x'][self.dim:2*self.dim])
        except TypeError:
            self.b = 0
            self.w = w
            self._log('Classification failed. C=%f'%self.complexity,
                      level=logging.CRITICAL)

        self.features = FeatureVector(numpy.atleast_2d(self.w.T).astype(
            numpy.float64), self.feature_names)
        try:
            wf = []
            for i,feature in enumerate(self.feature_names):
                if not float(self.w[i]) == 0:
                    wf.append((float(self.w[i]), feature))
            wf.sort(key=lambda (x, y): abs(x))
            if not len(wf) == 0 :
                w = numpy.array(wf,dtype='|S200')
            self.num_retained_features = len(wf)
        except ValueError :
            print 'w could not be converted.'
        except IndexError :
            print 'There are more feature names than features. Please check your feature generation and input data.'
            self.b = 0
            w = numpy.zeros(self.dim)
            self.w = w
        self.classifier_information["~~Num_Retained_Features~~"] = \
            self.num_retained_features
        self.print_w = w

# ===========================================================================

class SVR2BRMMNode(LibSVMClassifierNode):
    """ Simple wrapper around epsilon-SVR with BRMM parameter mapping """

    def __init__(self, range=3, complexity=1, **kwargs):
        super(SVR2BRMMNode, self).__init__(
            epsilon=(range-1.0)/(range+1.0),
            complexity=2.0*complexity/(range+1.0),
            svm_type ='epsilon-SVR',
            **kwargs)

@QNormalParameter("range_", mu=2, sigma=1, q=1)
class RMMClassifierMatlabNode(RegularizedClassifierBase):
    """ Classify with Relative Margin Machine using original matlab code

    This node integrates of the "original" Shivaswamy RMM code. This RMM is
    implemented in Matlab and uses the mosek optimization suite.

    For this node to work, make sure that

    1.  Matlab is installed.
    2.  The pymatlab Python package is installed properly. pymatlab can be
        downloaded from http://pypi.python.org/pypi/pymatlab/0.1.3
        For a MacOS setup guide please see
        https://svn.hb.dfki.de/IMMI-Trac/wiki/pymatlab
    3.  mosek is installed and matlab can access it. See http://mosek.com/
        People with university affiliation can request free academic licenses
        within seconds from http://license.mosek.com/cgi-bin/student.py

    **References**

        ========= ==================================================
        main
        ========= ==================================================
        author    Shivaswamy, P. K. and Jebara, T.
        title     `Maximum relative margin and data-dependent regularization <http://portal.acm.org/citation.cfm?id=1756031>`_
        journal   Journal of Machine Learning Research
        pages     747-788
        volume    11
        year      2010
        ========= ==================================================

    **Parameters**
        :complexity:
            Complexity sets the weighting of punishment for misclassification
            in comparison to generalizing classification from the data.
            Value in the range form 0 to infinity.

            (*optional, default: 1*)

        :range:
            Defines constraint radius for the outer boarder of the classification.
            Going to infinity, this classifier will be identical to the SVM.
            This parameter should be always greater then one.
            Going to one, the classifier will be a variant of the
            Regularized Linear Discriminant analysis.

            (*optional, default: 2*)

        .. note:: This classification node doesn't have nice debugging outputs
            as most errors will occur in mosek, i.e., from within the matlab
            session. In the rmm.m matlab code one might want to save the 'res'
            variable as it contains mosek error codes.

        .. note:: This implementation doesn't use class weights, i.e.,
            w=[1,1] is fixed.

    **Exemplary Call**

    .. code-block:: yaml

        -
            node :  RMMmatlab
            parameters :
                complexity : 1.0
                range : 2.0
                class_labels : ['Standard', 'Target']

    :input:    FeatureVector
    :output:   PredictionVector
    :Author: David Feess (David.Feess@dfki.de)
    :Revised: 2011/03/10

    """
    def __init__(self,range=2, **kwargs):

        super(RMMClassifierMatlabNode, self).__init__(**kwargs)

        self.set_permanent_attributes(range=range)

    def _stop_training(self, debug = False):
        """ Finish the training, i.e. train the RMM.
        Essentially, the data is passed to matlab, and the classification
        vector w and the offset b are returned."""

        self._log("Preprocessing of Matlab RMM")
        self._log("Instances of Class %s: %s, %s: %s" \
                  % (self.classes[0],
                     self.labels.count(self.classes.index(self.classes[0])),
                     self.classes[1],
                     self.labels.count(self.classes.index(self.classes[1]))))
        # Dimension of the data
        self.num_samples = len(self.samples)
        # Mapping from class to value of classifier
        self.bi = []
        for label in self.labels:
            #if self.classes.index(label) == 0:
            if label == 0:
                self.bi.append(-1)
            else:
                self.bi.append(1)
        self._log("optimization preparation")

        # Import the necessary matlab interface
        try:
            from pymatlab.matlab import MatlabSession
        except ImportError:
            raise Exception("Using the RMMClassifierMatlabNode requires the pymatlab module.")
        try:
            matlab = MatlabSession('matlab -nojvm -nodisplay')
        except OSError:
            raise Exception("pymatlab couldn't start matlab session. Is pymatlab configured properly?")

        matlab.run('clear')
        matlab.run("addpath('pySPACE/missions/nodes/classification/svm_variants/')")

        labels = numpy.array(self.bi)[:, numpy.newaxis]*1.0
        if self.kernel_type == "LINEAR":
            train_data = numpy.array(self.samples).T
            # Kzz = numpy.dot(train_data.T, train_data)
            matlab.putvalue('labels', labels)
            matlab.putvalue('train_data', train_data)
            matlab.run("[w_rmm,b_rmm] = compute_linear_RMM(labels, train_data, %f, %f);" % (self.complexity, self.range))

            self.w = matlab.getvalue('w_rmm')
            self.b = matlab.getvalue('b_rmm')
        else:
            raise NotImplementedError(
                "Nonlinear kernels have not been wrapped, yet. " +
                "The required matlab implementation is already provided.")
            # KZZ = numpy.zeros((self.num_samples,self.num_samples))
            # for i in range(self.num_samples):
            #     si=self.samples[i]
            #     for j in range(self.num_samples):
            #         if i>j:
            #             KZZ[i][j] = KZZ[j][i]
            #         else:
            #             KZZ[i][j] = self.kernel_func(si,self.samples[j])
        matlab.close()

        self._log("Construction complete")
        self.delete_training_data()

# ===========================================================================
@NoOptimizationParameter("version")
@NoOptimizationParameter("kernel_type")
class RmmPerceptronNode(RMM2Node, BaseNode):
    """ Online Learning variants of the 2-norm RMM

    **Parameters**

        .. seealso:: :class:`RMM2Node`

        :co_adaptive:
            Integrate backtransformation into classifier to catch
            changing preprocessing

            (*optional, default: False*)

        :co_adaptive_index:
            Number of nodes to go back

            (*optional, default: numpy.inf*)

        :history_index:
            Number of history index of original data used for co-adaptation

            (*optional, default: 1*)

    **Exemplary Call**

    .. code-block:: yaml

        -   node : RmmPerceptronNode
            parameters :
                range : 3
                complexity : 1.0
                weight : [1,3]
                class_labels : ['Standard', 'Target']

    :Author:    Mario Michael Krell
    :Created:   2014/01/02
    """
    def __init__(self, version="samples", kernel_type='LINEAR',
                 co_adaptive=False, co_adaptive_index=numpy.inf,
                 history_index=1, **kwargs):
        RMM2Node.__init__(self, version=version, kernel_type=kernel_type,
                          **kwargs)
        self.set_permanent_attributes(co_adaptive=co_adaptive,
                                      co_adaptive_index=co_adaptive_index,
                                      history_index=history_index,
                                      hist_w=None,
                                      hist_b=0,
                                      zero_s=None)

    def _train(self, data, class_label):
        """ Main method for incremental and normal training

        Code is a shortened version from the batch algorithm.
        """
        if self.co_adaptive:
            try:
                hist_data = copy.deepcopy(data.history[self.history_index-1])
            except IndexError:
                self._log("No history data available for classifier! " +
                          "Co-adaptivity is turned off.",
                          level=logging.CRITICAL)
                self.co_adaptive = False
        data_array = data.view(numpy.ndarray)
        # shortened initialization part from RegularizedClassifierBase
        if self.feature_names is None:
            try:
                self.feature_names = data.feature_names
            except AttributeError as e:
                warnings.warn("Use a feature generator node before a " +
                              "classification node.")
                raise e
            if self.dim is None:
                self.dim = data.shape[1]
        if class_label not in self.classes and not "REST" in self.classes:
            warnings.warn("Please give the expected classes to the classifier! "
                          + "%s unknown. "%class_label
                          + "Therefore define the variable 'class_labels' in "
                          + "your spec file, where you use your classifier. "
                          + "For further info look at the node documentation.")
            if not(len(self.classes) == 2):
                self.classes.append(class_label)
                self.set_permanent_attributes(classes=self.classes)
        # individual initialization of classification vector
        if self.w is None:
            self.w = numpy.zeros(self.dim, dtype=numpy.float)
        if self.co_adaptive and (self.hist_w is None or self.zero_s is None):
            if type(hist_data) == FeatureVector:
                self.hist_w = FeatureVector.replace_data(
                    hist_data, numpy.zeros(hist_data.shape, dtype=numpy.float))
                self.zero_s = FeatureVector.replace_data(
                    hist_data, numpy.zeros(hist_data.shape, dtype=numpy.float))
            elif type(hist_data) == TimeSeries:
                self.hist_w = TimeSeries.replace_data(
                    hist_data, numpy.zeros(hist_data.shape, dtype=numpy.float))
                self.zero_s = TimeSeries.replace_data(
                    hist_data, numpy.zeros(hist_data.shape, dtype=numpy.float))
        if self.samples is None:
            self.samples = ["empty"] # to suppress logging warning
        # update part init for compatibility with batch mode formulas
        i = 0
        weight = self.weight[self.classes.index(class_label)]
        self.ci = [float(self.complexity * weight)]
        if not self.squ_factor:
            M = [1.0/(numpy.linalg.norm(data_array)**2.0 + self.offset_factor)]
        else:
            M = [(1 / (numpy.linalg.norm(data_array)**2.0
                  + self.offset_factor + 1 / (2 * self.ci[i])),
                  1 / (numpy.linalg.norm(data_array)**2.0
                  + self.offset_factor + 1 /
                  (2 * self.ci[i] * self.complexity_correction)))]
        bi = float(self.classes.index(class_label) * 2 - 1)
        xi = data_array[0, :]
        fi = bi * (dot(xi.T, self.w) + self.b)
        s1 = 0.0
        s2 = 0.0
        beta = False
        old_dual = 0  # just used to have the same formulas as in batch version
        current_dual = numpy.zeros((1, 2))
        # update part
        if self.squ_factor:
            x = old_dual - self.omega * M[i][0] * (fi - 1 + s1)
        elif not self.squ_factor:
            x = old_dual - self.omega * M[i] * (fi - 1)
        # no update of alpha but update of beta (eventually zero update)
        if not x > 0:
            beta = True
            if self.squ_factor:
                x = old_dual - self.omega * M[i][1] * (
                    self.range - fi + s2)
            elif not self.squ_factor:
                x = old_dual - self.omega * M[i] * (
                    self.range - fi + s2)
        # map dual solution to the interval [0,C] in L1 case or
        # just make it positive in the L2 case
        # current_dual[i]=self.project(x,index=i)
        if x <= 0:
            current_dual[i] = [0, 0]
        elif not beta and not self.squ_factor:
            current_dual[i, 0] = min(x, self.ci[i])
        elif beta and not self.squ_factor:
            current_dual[i, 1] = min(x, self.ci[i] *
                                     self.complexity_correction)
        elif not beta and self.squ_factor:
            current_dual[i, 0] = x
        elif beta and self.squ_factor:
            current_dual[i, 1] = x
        # update w and b
        delta = (current_dual[i, 0] + current_dual[i, 1]
                 - old_dual) * bi
        # for beta:  difference needed
        if beta:
            delta = -delta
        # update classification function parameter w and b
        # self.update_classification_function(delta=delta, index=i)
        self.b += self.offset_factor * delta
        if not self.co_adaptive:
            self.w += delta * xi
        else:
            self.hist_b += delta
            self.hist_w += delta * hist_data
            self.update_from_history()

    def update_from_history(self):
        """ Update w from historic data to catch changing preprocessing """
        temp_w = self.get_previous_execute(
            data=self.hist_w, number=self.co_adaptive_index)
        temp_T = self.get_previous_execute(
            data=self.zero_s, number=self.co_adaptive_index)
        temp_w += (self.hist_b-1.0)*temp_T
        self.w = temp_w.view(numpy.ndarray)[0, :]

    def train(self,data,label):
        """ Prevent RegularizedClassifierBase method from being called """
        #one vs. REST case
        if "REST" in self.classes and not label in self.classes:
            label = "REST"
        # one vs. one case
        if not self.multinomial and len(self.classes) == 2 and \
                not label in self.classes:
            return
        start_time_stamp = timeit.default_timer()
        BaseNode.train(self, data, label)
        stop_time_stamp = timeit.default_timer()
        if not self.classifier_information.has_key("Training_time(classifier)"):
            self.classifier_information["Training_time(classifier)"] = \
                stop_time_stamp - start_time_stamp
        else:
            self.classifier_information["Training_time(classifier)"] += \
                stop_time_stamp - start_time_stamp

    def _inc_train(self, data, label):
        """ Incremental training and normal training are the same """
        #one vs. REST case
        if "REST" in self.classes and not label in self.classes:
            label = "REST"
        # one vs. one case
        if not self.multinomial and len(self.classes) == 2 and \
                not label in self.classes:
            return
        if self.co_adaptive == "double" or not self.co_adaptive:
            self._train(data, label)
        if self.co_adaptive:
            self.update_from_history()

    def _stop_training(self, debug=False):
        """ Do nothing than suppressing mother method """
        self.classifier_information["~~Solver_Iterations~~"] = 0
        try:
            self.classifier_information["~~offset~~"] = self.b
            self.classifier_information["~~w0~~"] = self.w[0]
            self.classifier_information["~~w1~~"] = self.w[1]
        except:
            pass

_NODE_MAPPING = {"1RMM": RMM1ClassifierNode,
                "2RMM": RMM2Node,
                "RMMmatlab": RMMClassifierMatlabNode,
                }


WHICH e-book reader? So many choices, so little time. . .
Now that I'm actually ready to own of these technological babies, the brain is starting to go into overload on the available options. There's the iPhone, the Kindle, the Nook, the Sony Reader, and even more it seems. Options of wireless capability. 500K free digital downloads, touchscreens, color capability, size, weight, available books for purchase, ability to work with multiple platforms, and more = fizzled brain.
Being I have friends and co-horts who own a Blackberry, an iPhone, and a Kindle (vs. the option of ONE device to meet all of these needs), how does one overcome that challenge? Really, who wants to carry those 3 devices AND possibly a laptop onto an airplane, public tranpsortation,or possibly to one's car (on a daily basis)? Honestly 1-2 devices should be enough. . . especially if the same person(s) are also toting an iPod too. At least I would be in that figurative bind.
After spending a good two hours trying to compare the various items, my ability to make a final decision still feels very fuzzy right now. If someone forced me to make a decision now, it would probably be the Nook….even though I REALLY like the Kindle.
If any of my tech savvy readers have a better argument for a different reader, please, please, please give a girl some help here. The goal? To only buy ONE e-book reader.
Just stumbled on your blog after searching on Google. I happen to run an ebook reader blog and notice you have a question on which ebook reader is best for you.
Feel free to reach me via the contact form on my blog with any specific questions you have. But generally speaking I would advise that you answer the following questions in order to narrow your reader choice down some.
1. What sort of book reading will you be doing? will it be mostly traditional fiction and non-fiction type books, work related stuff or for school.
2. Is format important to you, as far as being tied to one versus having many to choose from?
3. What about wireless connectivity, will you be using your reader as you jaunt around town or on a commute?
1. I will be using my future e-reader for work (i.e. agency-stuff), this includes primarily non-fiction works (books not in print YET, and books already printed/published in hardcover or paperback). If I can also download the NY Times, the Los Angeles Times, and other periodicals, that would a nice addition. Some fiction will be downloaded at various times, but not all the time obviously.
2. My main fear is not being able to download a particular book to my reader, when it may only be available on a different reader. While I do admit some very obscure books are very much enjoyed, there is also the need to be able to access any mainstream book too. I look at this decisoin at the difference between getting a MP3 player and an iPod. I chose the iPod, but know that anything I originally owned in MP3 format would not be iPod friendly when making this decision 7 yrs ago.
Any suggestions you'd like to make? What have you liked and/or found to be the best while writing your blog?


Reflecting its surroundings with a splintered and imperfect view is Jordan Griska's 2016 sculpture Wreck, a non-functional model of a Mercedes Benz S550 made entirely from reflective stainless steel.
The piece, which is composed of nearly 12,000 individual parts, is meant to highlight both luxury and mortality from a removed perspective. While researching the work Griska referenced Andy Warhol's series of car crash prints, connecting the sterility of his work's stainless steel to that of a lithoprint.


My 2-player micro-game on strategic aspects of war has changed its name from Politics By Other Means, adjudged too esoteric, to "The March of Progress" (new title courtesy of Charlie, my wife).
In addition, I'm now extending it with another scenario, so that we have a better progression – there was a gap for most of the 19th century. Now we have 18th century Limited War to Napoleonic, then American Civil War (the new one), through World War 1 and finally to World War 2. As there is an Introductory Scenario too, that's 6 scenarios, which I think is plenty for a micro-game.
Each scenario has various tweaks to make the strategy appropriate to the period. The new ACW one will probably be tweaked so that players start with Economic Points rather than VPs and lose them when they get their cards back by playing the Score card. The Confederacy can start with fewer Economic Points, so they want a quick war (ending by taking Washington quickly), while the Union want a longer affair so their economic muscle can take effect. That's the overall concept; much work to do on bringing this scenario to fruition.
In the meantime the Napoleonic scenario has been tested a lot more, and is very nearly finalised. Napoleon now gets an extra attack point for each French army he's using to attack with, in addition to the normal +1 for using the Attack +1 card.
Recent play-testing has suggested that, in order to avoid stalling (a slight problem for a couple of scenarios in certain circumstances) players should earn an automatic single VP when playing their Score card. This appears to be a very elegant solution! As ever, subject to playtesting.


Our dedicated team wants your visit to be as comfortable as possible, and we guarantee that the treatment you receive is second to none. We'll make sure you're looked after.
Chester has been Shepton Mallet Treatment Centre's Hospital Director since April 2008.
Having studied Maths and Statistics at the University of Exeter, Chester subsequently gained financial management and leadership experience in a range of business sectors.
Since 1995, Chester has worked in the UK independent healthcare sector.
Chester is particularly interested in developing a streamlined and efficient patient experience. He is passionate about implementing best practice, and delivering the highest quality service to patients.
Mr Ciampolini trained at the Medical School in Florence, where he achieved full marks with honours. As a postgraduate, he achieved the fellowship qualifications FRCS (Eng) and FRCS (Tr and Orth) from the Royal College of Surgeons.
Before joining the Peninsula NHS Treatment Centre, he was a hip and knee arthroplasty fellow at The Prince Charles Hospital, Brisbane.
Prior to that, he held Specialist Registrar posts at Derriford Hospital, the Princess Elizabeth Orthopaedic Centre in Exeter, and Torbay Hospital. Mr Ciampolini is a member of the British Orthopaedic Association, the British Medical Association and the Hospital Infection Society, and is also an Honorary Fellow of the Peninsula Medical School in Plymouth.
Additionally, he has written and co-written a number of publications related to his specialist field, and is currently working on a range of research projects.
Liz is responsible for professional clinical leadership at Shepton Mallet NHS Treatment Centre.
Liz trained at Radcliffe Infirmary in Oxford. Before joining Shepton Mallet, she worked in BMI Healthcare's Bath Clinic, where she was Director of Nursing from 1993.
Liz's management experience is hospital wide, with a particular focus on quality and accreditation processes.
Her experience includes significant involvement in the achievement of CQC standards and clinical governance practices across the board.
Liz is passionate about delivering excellent patient care, and is especially interested in how hospital-wide clinical governance can benefit clinical outcomes for patients.
Catherine is responsible for the professional administration and referral management leadership at Shepton Mallet Treatment Centre.
Catherine qualified with a BSc (Hons) in Adult Nursing from the University of the West of England in 1997.
Before joining Shepton Mallet, Catherine was a Ward manager on an elective care ward at Yeovil District Hospital for 10 years.
Since then she has managed the Patient Experience Team, Outpatients, Clinical Governance and now the Operational team and Stores.
Catherine is passionate about process development and delivering high quality, safe and effective patient care, she has been highly involved in the CQC preparation and the development of clinical outcome reporting within the centre.
Trudy joined Shepton Mallet Treatment Centre in 2006 as Clinical Effectiveness Manager.
She trained at Frenchay Hospital, Bristol and went on to specialise in theatre nursing gaining the formal ENB Operating Department Qualification.
Before joining Shepton Mallet, she worked in senior nurse management positions within Bupa Healthcare at their Bristol Hospital and attained a BTEC in Business Management, before moving on to a Surgical Modernisation Manager role in a local District General Hospital.
Having also worked as a Senior Management Consultant on the National Joint Registry project, she is committed to the development and management of processes to ensure robust reporting of clinical outcomes, which she believes is pivotal in the provision of high quality, safe and effective practice.
Dr Jobson studied Medicine at Cambridge University and then completed clinical training at Oxford University. After House Officer posts he completed his training in Medicine and Gastroenterology in Nottingham and allied hospitals in the Trent Region. During that time he spent 3 years undertaking research which led to a PhD.
He then took up a Consultant Physician post at Musgrove Park Hospital in Taunton where he currently works with subspecialty interests in Upper GI Cancer, Endoscopy and Hepatology. He has worked in a variety of management positions, including Deputy Medical Director at Musgrove Park Hospital.
Since 2014 Dr Jobson has been undertaking endoscopy in various treatment centres for Care UK, and was appointed their Clinical Director for Endoscopy in September 2016.
Mr Szabolcs Szigethy was born in Tatabanya, Hungary. He qualified from Medical School of Szeged, Hungary and was granted his Certificate of Completion of Training by the Hungarian National Specialist Board, Registration Number 1332, in 1998.
After gaining valuable experience training in four centres in Hungary, he moved to the UK to further his experience and has spent time in NHS hospitals in England and Scotland.
As a fully accredited ENT surgeon, he manages diseases of the ear, nose and throat in adults and children. He is dedicated to providing comprehensive, state of the art medical and surgical care for his patients.
Mr Szigethy treats a broad spectrum of ear, nose and throat disorders and conditions, and as a part of this he performs a wide range of procedures, which among others include tonsillectomy, adenoidectomy, grommet insertion, and tympanoplasty; however his specialist interest is nasal form and function. In his view, modern nasal surgery takes into account not just the cosmetic appearance of the nose, but also seeks to improve the natural functions of the nose.
He carries out functional endoscopic sinus surgery (FESS), rhinoplasty, septoplasty, snoring investigation and surgery, allergy investigation and treatment.
Mr Szigethy has enjoyed the managerial and leadership role of his posts as a consultant and recognises good management skills to be fundamental to consultants in modern health care.
To underpin his practical performance in this area he secured an MBA degree in 2004 when he specialised in management, finance and economics.
His MBA thesis title was "A customer centred approach in health care: a proposal to apply the fundamental principles of TQM philosophy in in-patient departments to achieve a patient-centred approach in health care". He is also a fellow of the Royal Society of Medicine.
Mr Dartey trained at the University of Ghana Medical School where he achieved a Bachelor of Medicine and Bachelor of Surgery degree.
After practicing internal medicine, trauma/emergency and obstetrics, and gynaecology for a few years he relocated to the UK.
Mr Dartey has worked in various teaching and district hospitals in England and Scotland and has carried out research as well as writing papers for publication.
He is well known in the region having worked at Musgrove Park Hospital in Taunton and RUH in Bath for four years.
Elisabeth Adams qualified in Medicine from St John's College, Cambridge in 1989.
She worked in Cambridge and then went up to Liverpool where she completed training in Obstetrics and Gynaecology, MRCOG and sub-speciality training in Urogynaecology in 2001. Prior to this she also completed 4 years of research and an MD in 1996.
She moved to UHBristol in July 2016 after 15 years as a consultant in Liverpool. As well as general gynaecology, she specialises in Prolapse and incontinence, chronic pelvic nerve pain and perineal and labial issues, along with psychosexual counselling. Outside her clinical work, she has a special interest in Clinical governance. She started work in June 2016 at Shepton Mallet treatment centre.
Mr Mujahid El-Deen worked in Scotland as an NHS consultant for eight years prior to joining Care UK.
He has extensive experience in trauma including treatment of peri-prosthetic fractures and simple and complex primary joint arthroplasty.
While in NHS post, he taught junior doctors and medical students from Glasgow University.
FRCS (Trauma and Orthopaedic) Edinburgh, 2008.
Master Degree in Orthopaedic Engineering gained from Cardiff University year, 2000.
FRCS Fellow of the Royal College of Physicians and Surgeons of Glasgow, 1995.
Knee replacement primary and revision surgery and computer navigation surgery, six months fellowship in Glasgow, 2006.
Paediatric orthopaedic fellowship, six months in Glasgow, 2010.
Mr Lupparelli trained at the Medical School in Rome where he achieved his Degree in Medicine and Surgery (full marks with honours), Licence to Practice in 1992 and his Diploma in Trauma and Orthopaedic Surgery (full marks with honours) in 1997.
He subsequently worked as a Consultant Orthopaedic Surgeon in Perugia (Italy) from 1998 to 2005 in a high-volume orthopaedic treatment centre. He then moved to L'Aquila University (Italy) in 2006 where he was a part-time university lecturer until 2008, also continuing his surgical practice as a Consultant Orthopaedic Surgeon.
He registered with the General Medical Council (UK) in 2003 (Full and Specialist Registration with Licence n°6070697) and worked on a part-time basis both in the NHS (2004) and in the UK Independent Sector Treatment Centre (2005) until he joined Peninsula NHS Treatment Centre in January 2009. Mr Lupparelli then joined Shepton Mallet Treatment Centre in 2011, where he has since held a full-time substantive position as a Consultant Orthopaedic Surgeon.
His special areas of interest include knee and shoulder surgery. He constantly updates his knowledge and expertise by completing CPD courses, conferences and online learning in keeping with his professional body recommendations.
He is a Fellow of The Royal College of Surgeons of England (Fellowship n° 9059891) and Member of the Medical Defence Union (Membership n° 640892I) in the UK. He is a Member of the European Society of Sports, Knee, Arthroscopy (ESSKA). He is also an International Member of the American Academy of Orthopaedic Surgeons (AAOS), a Fellow of the Royal Society of Medicine (RSM) and a Member of the Royal College of Physicians (RCP).
Mr Lupparelli has so far authored and co-authored a number of papers in international peer-reviewed journals and has presented numerous papers at national and international conferences. He is in the Advisory Board of the Cochrane Musculo-Skeletal Group (CMSG) and is an independent scientific consultant to the Gerson Lehrman Group Healthcare and Biomedical Council (USA).
Mr. Kavouriadis graduated with medical degree from Ethnikon and Kapodistriakon University in Athens, Greece in 1995. Since then he has obtained specialist certificates in Orthopaedic and trauma surgery from the University of Athens.
He became an Orthopaedic and Trauma Consultant in 2005 after completing his training in several major Orthopaedic and trauma units in Athens and spending six months as a trainee in Humboldt University in Berlin, at Charite' University Hospital, training in Joint replacement surgery and arthroscopy.
Mr. Kavouriadis began working as a Trauma and Orthopaedic Fellow in Queen's Hospital Burton on Trent in 2005 before moving to Paisley, Scotland, to work as a full time Consultant Trauma and Orthopaedic Surgeon at the Royal Alexandra Hospital from 2007 until 2011.
He then moved to Manchester NHS Clinical Assessment and Treatment Service, where he participated in teaching nurses and physiotherapists.
During this time he also performed surgical procedures as a part time consultant at Eccleshill Treatment Centre.
He is actively interested in lean techniques in the health service, effective use of resources and efficient health service processes and pathways. He is a postgraduate student at the Manchester Business School and has a special interest in distal monitoring and treatment.
Mr Lattouf is originally from Lebanon but completed his medical studies and orthopaedic training in Lithuania. During his training Mr Lattouf had clinical attachments in France and Lebanon. From August 2003 Mr Lattouf worked in Lithuania as a Consultant Orthopaedic Surgeon before moving to the UK in June 2007 to work as a Locum Orthopaedic SPr. In January 2009 he started a one-year foot and ankle fellowship at Stepping Hill Hospital, Stockport. During his fellowship he attended foot and ankle courses and training in Switzerland. Mr Lattouf has also been a member of BOFAS (British Orthopaedic Foot and Ankle Society) since September 2010.
Having qualified from Silesian Academy of Medicine in Katowice, Poland, Mr Rozycki undertook postgraduate training in orthopaedic and trauma surgery in the Provincial Hospital No 4 in Bytom, Poland. He became a Consultant Orthopaedic Surgeon in 2002. During his career in Poland he specialised in trauma surgery and minor orthopaedic procedures. Mr Rozycki is a member of the Polish Society of Orthopaedic Surgeons. From 2005 to 2009 he worked in Shepton Mallet NHS Treatment Centre as an Associate Specialist and then Joint Fellow, developing his skills in joint arthroplasty. In November 2009 Mr Rozycki was promoted to the position of Consultant Orthopaedic Surgeon at UK Specialist Hospitals (now Care UK).
Mr Kvas graduated from the Medical School Komensky University Bratislava in 1984. He was a Surgical Trainee at university hospitals in Slovakia and obtained his full specialist qualification in 1993. Between 1994 and 2005 he provided complex general surgical and endoscopy services as a Consultant Surgeon in NHS Malta.
He was appointed as Consultant Surgeon in Western Isles Hospital, North West Scotland in August 2005 where he developed breast and endoscopy services. Among various surgical activities Mr Kvas was involved in The Scottish National Bowel Cancer Screening Programme as an Endoscopist in 2008 and 2009. He has been serving as a Consultant Surgeon in Greater Manchester Surgical Centre (NHS Treatment Centre) since April 2009.
Dr Knoell graduated from the Medical Faculty of J.W. Goethe University in Frankfurt am Main, Germany in 1986. She started her medical career in general medicine and paediatrics and then began her training in Anaesthesia and Intensive Medicine and Emergency Medicine in 1990.
Dr Knoell qualified as a specialist in Anaesthesia (Facharzt) 1996 and began working as a Consultant in Anaesthesia and Intensive Care Unit at the General District Hospital Erlenbach am Main, Germany.
After moving to the UK in 2007, Dr Knoell took up the position of Consultant Anaesthetist at Cheshire Merseyside NHS Treatment Centre. From 2011-2012 she was Consultant Anaesthetist at St Mary's NHS Hospital Isle of Wight. She joined Shepton Mallet NHS Treatment Centre as a Consultant Anaesthetist in April 2012.
Dr Vereczkey completed his undergraduate medical training at the Semmelweis Medical University in Budapest, Hungary in 1984. He worked at the Postgraduate Medical Institute in Budapest and in 1989 he obtained his specialist registration in anaesthesia and intensive care at Wexham Park Hospital, Slough and in King Edward VII Hospital, Windsor.
In 1992 he acquired the Diploma of the European Academy of Anaesthesiology and in 1996 he obtained a specialist registration in Cardiology. He was the Head of ICU at the Cardiovascular Surgery Department at the National Institute of Health in Budapest.
In his 16 years of experience as a specialist anaesthetist he has worked in almost all fields of anaesthesia and intensive therapy performing more than 6000 EDA-Spinal procedures in obstetrics and orthopaedics. He has two inventions, a 3D-ECG and an ICG-Plethysmograph to assess endothelial dysfunction.
Dr Vereczkey joined Shepton Mallet NHS Treatment Centre as Consultant Anaesthetist in October 2005.
Dr Ros graduated University of Medicine and Pharmacy in Cluj Napoca in 1997. She underwent her training programme in anaesthetics and intensive care medicine in Cluj Napoca and became consultant in anaesthetics and intensive care in 2004. As a trainee she was granted the European Diploma in anaesthetics and intensive care after passing the exam and became a member of the European Academy of Anaesthetists in 2003.
Between 2004 and 2006 she worked as a locum consultant in the university hospital of general surgery ''Professor Dr Octavian Fodor'' Cluj Napoca which is a specialised tertiary centre in gastrointestinal and hepatic surgery.
In 2006 Dr Ros was appointed as a consultant anaesthetist in the University Oncology Institute ''Professor I.Chiricuta'' Cluj Napoca in the anaesthetic and intensive care department.
From 2011 to 2015 she worked in Yeovil District Hospital NHS Trust in both anaesthetic and intensive care unit.
She was appointed as a consultant anaesthetist in Shepton Mallet Treatment Centre in 2015.
Chris Parker qualified from Guy's Hospital Medical School, London in 1979. He undertook surgical training in London, Cambridge and the South East, completing his Urological training in 1993 at the Institute of Urology at the Middlesex and University College Hospitals, London. At the end of his training he won the Keith Yeates Medal (awarded for 'Outstanding performance in the FRCS Urology exam). Chris then moved to Somerset where he was Consultant Urologist at Yeovil District Hospital from 1993 to late 2016, during which time he was also Medical Director from 2000 to 2003. Chris started working at Shepton Mallet Treatment Centre in May 2016.
Mr Balogh graduated from the Medical University of Debrecen, Hungary in 1989. He qualified in Radiology in 1993, and Radiation Oncology in 2001. He has attended numerous international postgraduate courses and congresses, including the annual ECR in Vienna and the ESHNR Course in Oxford. He was granted an honorary observer post at Addenbrookes Hospital, Cambridge in 1997. Mr Balogh specialised in Musculoskeletal and General Radiology, Neuroradiology and Oncoradiology: CT, MRI, US, DSA, CR, Mammography, Fluoroscopy, Image Guided FNA/Core Biopsy, Interventional Procedures, Conformal Treatment Planning, Radiation Therapy Simulation, Percutaneous Radiation Therapy, Brachytherapy, Radiochemotherapy. Mr Balogh has engaged in numerous research activities, typically as a radiologist and clinical co-investigator, mainly for tumor assessment by CT and MRI in various clinical trials for radiotherapy, chemotherapy and molecular target therapies.


text:
STEAMING AHEAD
Steam Cyber Monday deals – Sims 4 for just $4, Destiny for $20, and Witcher 3 for $8
Charlotte Edwards, Technology and Science Reporter
Updated: 15:52, 29 Nov 2021
STEAM has some excellent Cyber Monday deals for gaming fans.
We've rounded up some of the best bargains on the site.
You could be playing The Witcher 2 for just $7.99Credit: CD Projekt Red
Sims 4 deal
Fans of this cult classic game may be pleased to see that Steam has a very impressive bargain on Sims 4.
The online gaming platform is currently selling the game for $4.79.
That's a staggering 88% off.
You'll need to be quick though as the offer ends on December 1.
Sims 4 on Steam for $4.79 – Buy Now
Most read in Tech
Witcher 3 deal
The second season of The Witcher will be released on Netflix next month.
If you can't wait for that you may want to check out this deal on The Witcher 3: Wild Hunt game.
Steam is selling the online game for $7.99.
That's a saving of 80% as it's usually priced at $39.99.
As with the Sims 4 deal, the offer ends on December 1.
The Witcher 3: Wild Hunt on Steam for $7.99 – Buy Now
Destiny 2: Beyond Light
Steam is currently offering the popular Destiny 2: Beyond Light for $19.99.
You will need to download the base Destiny game from Steam in order to add Beyond Light on top but luckily the base game is free to play on the platform.
This 50% off offer also ends on December 1.
Destiny 2: Beyond Light – Buy Now
All prices in this article were correct at the time of writing but may have since changed. Always do your own research before making any purchase.
Destiny 2 can currently be played for free on SteamCredit: Bungie
FedEx packages dumped in Alabama woods – 400 parcels found in Hayden amid Black Friday & Cyber Monday delivery surge
In other news, we revealed how to protect yourself from Cyber Monday scammers.
Android users are being warned to update their phone's privacy settings after a new update could leave their devices vulnerable.
And, scientists are using the winds on Mars to map out the first complete picture of how it was formed three billion years ago.

redpajama_set_name: RedPajamaCommonCrawl