Source code for mdt.model_building.parameter_functions.transformations
import numpy as np
__author__ = 'Robbert Harms'
__date__ = "2014-06-20"
__license__ = "LGPL v3"
__maintainer__ = "Robbert Harms"
__email__ = "robbert@xkls.nl"
[docs]class AbstractTransformation:
"""The transformations define the encode and decode operations needed to build a codec.
These objects define the transformation to and from model and optimization space.
"""
[docs] def get_cl_encode(self):
"""Get the CL encode assignment constructor
Returns
AssignmentConstructor: The cl code assignment constructor for encoding the parameter.
"""
raise NotImplementedError()
[docs] def get_cl_decode(self):
"""Get the CL decode assignment constructor
Returns:
AssignmentConstructor: The cl code assignment constructor for decoding the parameter.
"""
raise NotImplementedError()
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
"""Encode the given bounds into the encoded parameter space.
Args:
lower_bounds (floar or ndarray): either a single bound, or per voxel a bound
upper_bounds (floar or ndarray): either a single bound, or per voxel a bound
Returns:
tuple: the transformed lower and upper bounds, same shape as inputs
"""
raise NotImplementedError()
[docs]class AssignmentConstructor:
[docs] def create_assignment(self, parameter_variable, lower_bound, upper_bound):
"""Create the assignment string.
Args:
parameter_variable (str): the name of the parameter variable holding the current value in the kernel
lower_bound (str): the value or the name of the variable holding the value for the lower bound
upper_bound (str): the value or the name of the variable holding the value for the upper bound
Returns:
str: the transformation assignment
"""
raise NotImplementedError()
[docs]class FormatAssignmentConstructor(AssignmentConstructor):
def __init__(self, assignment):
"""Assignment constructor that formats the given assignment template.
This expects that the assignment string has elements like:
* ``{parameter_variable}``: for the parameter variable
* ``{lower_bound}``: for the lower bound
* ``{upper_bound}``: for the upper bound
Args:
assignment (str): the string containing the assignment template.
"""
self._assignment = assignment
[docs] def create_assignment(self, parameter_variable, lower_bound, upper_bound):
assignment = self._assignment.replace('{parameter_variable}', parameter_variable)
assignment = assignment.replace('{lower_bound}', str(lower_bound))
assignment = assignment.replace('{upper_bound}', str(upper_bound))
return assignment
[docs]class IdentityTransform(AbstractTransformation):
"""The identity transform does no transformation and returns the input given."""
[docs]class PositivityTransform(AbstractTransformation):
"""Restrain the parameter to the positive values, i.e. returns ``max(x, 0)``."""
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor('max({parameter_variable}, (mot_float_type)0)')
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('max({parameter_variable}, (mot_float_type)0)')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, upper_bounds
[docs]class ClampTransform(AbstractTransformation):
"""The clamp transformation limits the parameter between its lower and upper bound using the clamp function."""
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor('clamp((mot_float_type){parameter_variable}, '
'(mot_float_type){lower_bound}, '
'(mot_float_type){upper_bound})')
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('clamp((mot_float_type){parameter_variable}, '
'(mot_float_type){lower_bound}, '
'(mot_float_type){upper_bound})')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, np.ones_like(lower_bounds) * np.inf
[docs]class ScaleClampTransform(AbstractTransformation):
def __init__(self, scale):
"""Clamps the value to the given bounds and applies a scaling to bring the parameters in sensible ranges.
To encode, the parameter value is multiplied by the scaling factor.
To decode, it is divided by the scaling factor.
Args:
scale (float): the scaling factor by which to scale the parameter
"""
super().__init__()
self._scale = scale
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor('clamp((mot_float_type){parameter_variable}, '
'(mot_float_type){lower_bound}, '
'(mot_float_type){upper_bound}) * ' + str(self._scale))
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('clamp((mot_float_type){parameter_variable} / ' + str(self._scale) + ', '
'(mot_float_type){lower_bound}, '
'(mot_float_type){upper_bound})')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, np.ones_like(lower_bounds) * np.inf
[docs]class ScaleTransform(AbstractTransformation):
def __init__(self, scale):
"""Applies a scaling to bring the parameters in sensible ranges.
To encode, the parameter value is multiplied by the scaling factor.
To decode, it is divided by the scaling factor.
Args:
scale (float): the scaling factor by which to scale the parameter
"""
super().__init__()
self._scale = scale
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor('{parameter_variable} * ' + str(self._scale))
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('{parameter_variable} / ' + str(self._scale))
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return lower_bounds * self._scale, upper_bounds * self._scale
[docs]class CosSqrClampTransform(AbstractTransformation):
"""The clamp transformation limits the parameter between its lower and upper bound using a cos(sqr()) transform."""
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor(
'acos(clamp((mot_float_type)sqrt(fabs( ({parameter_variable} - {lower_bound}) / '
' ({upper_bound} - {lower_bound}) )), '
' (mot_float_type)0, (mot_float_type)1))')
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('pown(cos({parameter_variable}), 2) * ' +
'({upper_bound} - {lower_bound}) + {lower_bound}')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, np.ones_like(upper_bounds) * np.inf
[docs]class SinSqrClampTransform(AbstractTransformation):
"""The clamp transformation limits the parameter between its lower and upper bound using a sin(sqr()) transform."""
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor(
'asin(clamp((mot_float_type)sqrt(fabs( ({parameter_variable} - {lower_bound}) / '
' ({upper_bound} - {lower_bound}) )), '
' (mot_float_type)0, (mot_float_type)1))')
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('pown(sin({parameter_variable}), 2) * ' +
'({upper_bound} - {lower_bound}) + {lower_bound}')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, np.ones_like(upper_bounds) * np.inf
[docs]class SqrClampTransform(AbstractTransformation):
"""The clamp transformation limits the parameter between its lower and upper bound using a sqr() transform."""
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor('clamp((mot_float_type)({parameter_variable} * {parameter_variable}), '
' (mot_float_type){lower_bound}, '
' (mot_float_type){upper_bound})')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, np.ones_like(upper_bounds) * np.inf
[docs]class AbsModXTransform(AbstractTransformation):
def __init__(self, x_cl, x_python):
"""Create an transformation that returns the absolute modulo x value of the input."""
super().__init__()
self._x_cl = x_cl
self._x_python = x_python
[docs] def get_cl_encode(self):
return FormatAssignmentConstructor(
'({parameter_variable} - (' + str(self._x_cl) + ' * floor({parameter_variable} / '
+ str(self._x_cl) + ')))')
[docs] def get_cl_decode(self):
return FormatAssignmentConstructor(
'({parameter_variable} - (' + str(self._x_cl) + ' * floor({parameter_variable} / '
+ str(self._x_cl) + ')))')
[docs] def encode_bounds(self, lower_bounds, upper_bounds):
return np.ones_like(lower_bounds) * -np.inf, np.ones_like(upper_bounds) * np.inf
[docs]class AbsModPiTransform(AbsModXTransform):
def __init__(self):
super().__init__('M_PI', np.pi)
[docs]class AbsModTwoPiTransform(AbsModXTransform):
def __init__(self):
super().__init__('(2 * M_PI)', 2 * np.pi)