Source code for mlprodict.grammar.gtypes

"""
Types definition.


:githublink:`%|py|5`
"""
import numpy
from .api_extension import AutoType


[docs]class MLType(AutoType): """ Base class for every type. :githublink:`%|py|12` """
[docs] def validate(self, value): """ Checks that the value is of this type. :githublink:`%|py|17` """ # It must be overwritten. self._cache = value
[docs] def cast(self, value): """ Converts *value* into this type. :githublink:`%|py|24` """ raise NotImplementedError() # pragma: no cover
[docs]class MLNumType(MLType): """ Base class for numerical types. :githublink:`%|py|31` """
[docs] def _format_value_json(self, value, hook=None): return str(value)
[docs] def _format_value_c(self, value, hook=None): return str(value)
[docs] def _copy_c(self, src, dst, hook=None): if hook == "typeref": return "*{0} = {1};".format(dst, src) return "{0} = {1};".format(dst, src)
[docs]class MLNumTypeSingle(MLNumType): """ int32 or float32 :githublink:`%|py|48` """
[docs] def __init__(self, numpy_type, name, ctype, key): self.numpy_type = numpy_type self.name = name self.ctype = ctype self.key = key
@property def CTypeSingle(self): """ Returns *ctype*. :githublink:`%|py|60` """ return self.ctype
[docs] def validate(self, value): """ Checks that the value is of this type. :githublink:`%|py|66` """ MLNumType.validate(self, value) if not isinstance(value, self.numpy_type): raise TypeError( # pragma: no cover "'{0}' is not a {1}.".format( type(value), self.numpy_type)) return value
[docs] def cast(self, value): """ Exports *value* into this type. :githublink:`%|py|77` """ if isinstance(value, numpy.float32): raise TypeError( # pragma: no cover "No need to cast, already a {0}".format(self.numpy_type)) if isinstance(value, numpy.ndarray): if len(value) != 1: raise ValueError( # pragma: no cover "Dimension of array must be one single {0}".format(self.numpy_type)) return value[0] raise NotImplementedError( # pragma: no cover "Unable to cast '{0}' into a {0}".format(type(self.numpy_type)))
[docs] def softcast(self, value): """ Exports *value* into this type, does it anyway without verification. :githublink:`%|py|92` """ if isinstance(value, numpy.ndarray): v = value.ravel() if len(v) != 1: raise ValueError( # pragma: no cover "Cannot cast shape {0} into {1}".format( value.shape, self.numpy_type)) return self.numpy_type(v[0]) return self.numpy_type(value)
[docs] def _export_common_c(self, ctype, hook=None, result_name=None): if hook == 'type': return {'code': ctype} if result_name is None else {'code': ctype + ' ' + result_name} if result_name is None: return {'code': ctype} return {'code': ctype + ' ' + result_name, 'result_name': result_name}
[docs] def _byref_c(self): return "&"
[docs] def _export_json(self, hook=None, result_name=None): return 'float32'
[docs] def _export_c(self, hook=None, result_name=None): if hook == 'typeref': return {'code': self.ctype + '*'} if result_name is None else {'code': self.ctype + '* ' + result_name} return self._export_common_c(self.ctype, hook, result_name)
[docs] def _format_value_json(self, value, hook=None): if hook is None or self.key not in hook: return value return hook[self.key](value)
[docs] def _format_value_c(self, value, hook=None): if hook is None or self.key not in hook: return "({1}){0}".format(value, self.ctype) return hook[self.key](value)
[docs]class MLNumTypeFloat32(MLNumTypeSingle): """ A numpy.float32. :githublink:`%|py|134` """
[docs] def __init__(self): MLNumTypeSingle.__init__( self, numpy.float32, 'float32', 'float', 'float32')
[docs]class MLNumTypeInt32(MLNumTypeSingle): """ A numpy.int32. :githublink:`%|py|144` """
[docs] def __init__(self): MLNumTypeSingle.__init__(self, numpy.int32, 'int32', 'int', 'int32')
[docs]class MLNumTypeBool(MLNumTypeSingle): """ A numpy.bool. :githublink:`%|py|153` """
[docs] def __init__(self): MLNumTypeSingle.__init__(self, numpy.bool, 'BL', 'bool', 'bool')
[docs]class MLTensor(MLType): """ Defines a tensor with a dimension and a single type for what it contains. :githublink:`%|py|162` """
[docs] def __init__(self, element_type, dim): if not isinstance(element_type, MLType): raise TypeError( # pragma: no cover 'element_type must be of MLType not {0}'.format(type(element_type))) if not isinstance(dim, tuple): raise TypeError( # pragma: no cover 'dim must be a tuple.') if len(dim) == 0: raise ValueError( # pragma: no cover "dimension must not be null.") for d in dim: if d == 0: raise ValueError( # pragma: no cover "No dimension can be null.") self.dim = dim self.element_type = element_type
@property def CTypeSingle(self): """ Returns *ctype*. :githublink:`%|py|185` """ return self.element_type.ctype
[docs] def validate(self, value): """ Checks that the value is of this type. :githublink:`%|py|191` """ MLType.validate(self, value) if not isinstance(value, numpy.ndarray): raise TypeError( # pragma: no cover "value is not a numpy.array but '{0}'".format(type(value))) if self.dim != value.shape: raise ValueError( # pragma: no cover "Dimensions do not match {0}={1}".format(self.dim, value.shape)) rvalue = value.ravel() for i, num in enumerate(rvalue): try: self.element_type.validate(num) except TypeError as e: # pragma: no cover raise TypeError( 'Unable to convert an array due to value index {0}: {1}'.format(i, rvalue[i])) from e return value
[docs] def _byref_c(self): return ""
[docs] def _format_value_json(self, value, hook=None): if hook is None or 'array' not in hook: return value return hook['array'](value)
[docs] def _format_value_c(self, value, hook=None): return "{{{0}}}".format(", ".join(self.element_type._format_value_c(x) for x in value))
[docs] def _export_json(self, hook=None, result_name=None): return '{0}:{1}'.format(self.element_type._export_json(hook=hook), self.dim)
[docs] def _export_c(self, hook=None, result_name=None): if len(self.dim) != 1: raise NotImplementedError( # pragma: no cover 'Only 1D vector implemented.') if hook is None: raise ValueError( # pragma: no cover "hook must contains either 'signature' or 'declare'.") if hook == 'signature': if result_name is None: raise ValueError( # pragma: no cover "result_name must be specified.") return {'code': "{0}[{1}] {2}".format(self.element_type._export_c(hook=hook)['code'], self.dim[0], result_name), 'result_name': result_name} elif hook == 'declare': if result_name is None: raise ValueError( # pragma: no cover "result_name must be specified.") dc = self.element_type._export_c( hook=hook, result_name=result_name) return {'code': "{0}[{1}]".format(dc['code'], self.dim[0])} elif hook == 'type': return {'code': "{0}*".format(self.element_type._export_c(hook=hook)['code'])} elif hook == 'typeref': if result_name is None: return {'code': "{0}*".format(self.element_type._export_c(hook='type')['code'])} code = self.element_type._export_c(hook='type')['code'] return {'code': "{0}* {1}".format(code, result_name), 'result_name': result_name} else: raise ValueError( # pragma: no cover "hook must contains either 'signature' or 'declare' not '{0}'.".format(hook))
[docs] def _copy_c(self, src, dest, hook=None): if len(self.dim) != 1: raise NotImplementedError( # pragma: no cover 'Only 1D vector implemented.') code = self.element_type._export_c(hook='type')['code'] return "memcpy({1}, {0}, {2}*sizeof({3}));".format(src, dest, self.dim[0], code)