Coverage for mlprodict/grammar/grammar_sklearn/grammar/gtypes.py: 86%
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
Shortcuts on this page
r m x toggle line displays
j k next/prev highlighted chunk
0 (zero) top of page
1 (one) first highlighted chunk
1"""
2@file
3@brief Types definition.
4"""
5import numpy
6from .api_extension import AutoType
9class MLType(AutoType):
10 """
11 Base class for every type.
12 """
14 def validate(self, value):
15 """
16 Checks that the value is of this type.
17 """
18 # It must be overwritten.
19 self._cache = value
21 def cast(self, value):
22 """
23 Converts *value* into this type.
24 """
25 raise NotImplementedError() # pragma: no cover
28class MLNumType(MLType):
29 """
30 Base class for numerical types.
31 """
33 def _format_value_json(self, value, hook=None):
34 return str(value)
36 def _format_value_c(self, value, hook=None):
37 return str(value)
39 def _copy_c(self, src, dst, hook=None):
40 if hook == "typeref":
41 return "*{0} = {1};".format(dst, src)
42 return "{0} = {1};".format(dst, src)
45class MLNumTypeSingle(MLNumType):
46 """
47 int32 or float32
48 """
50 def __init__(self, numpy_type, name, ctype, key):
51 self.numpy_type = numpy_type
52 self.name = name
53 self.ctype = ctype
54 self.key = key
56 @property
57 def CTypeSingle(self):
58 """
59 Returns *ctype*.
60 """
61 return self.ctype
63 def validate(self, value):
64 """
65 Checks that the value is of this type.
66 """
67 MLNumType.validate(self, value)
68 if not isinstance(value, self.numpy_type):
69 raise TypeError( # pragma: no cover
70 "'{0}' is not a {1}.".format(
71 type(value), self.numpy_type))
72 return value
74 def cast(self, value):
75 """
76 Exports *value* into this type.
77 """
78 if isinstance(value, numpy.float32):
79 raise TypeError( # pragma: no cover
80 "No need to cast, already a {0}".format(self.numpy_type))
81 if isinstance(value, numpy.ndarray):
82 if len(value) != 1:
83 raise ValueError( # pragma: no cover
84 "Dimension of array must be one single {0}".format(self.numpy_type))
85 return value[0]
86 raise NotImplementedError( # pragma: no cover
87 "Unable to cast '{0}' into a {0}".format(type(self.numpy_type)))
89 def softcast(self, value):
90 """
91 Exports *value* into this type, does it anyway without verification.
92 """
93 if isinstance(value, numpy.ndarray):
94 v = value.ravel()
95 if len(v) != 1:
96 raise ValueError( # pragma: no cover
97 "Cannot cast shape {0} into {1}".format(
98 value.shape, self.numpy_type))
99 return self.numpy_type(v[0])
100 return self.numpy_type(value)
102 def _export_common_c(self, ctype, hook=None, result_name=None):
103 if hook == 'type':
104 return {'code': ctype} if result_name is None else {'code': ctype + ' ' + result_name}
105 if result_name is None:
106 return {'code': ctype}
107 return {'code': ctype + ' ' + result_name, 'result_name': result_name}
109 def _byref_c(self):
110 return "&"
112 def _export_json(self, hook=None, result_name=None):
113 return 'float32'
115 def _export_c(self, hook=None, result_name=None):
116 if hook == 'typeref':
117 return {'code': self.ctype + '*'} if result_name is None else {'code': self.ctype + '* ' + result_name}
118 return self._export_common_c(self.ctype, hook, result_name)
120 def _format_value_json(self, value, hook=None):
121 if hook is None or self.key not in hook:
122 return value
123 return hook[self.key](value)
125 def _format_value_c(self, value, hook=None):
126 if hook is None or self.key not in hook:
127 return "({1}){0}".format(value, self.ctype)
128 return hook[self.key](value)
131class MLNumTypeFloat32(MLNumTypeSingle):
132 """
133 A numpy.float32.
134 """
136 def __init__(self):
137 MLNumTypeSingle.__init__(
138 self, numpy.float32, 'float32', 'float', 'float32')
141class MLNumTypeFloat64(MLNumTypeSingle):
142 """
143 A numpy.float64.
144 """
146 def __init__(self):
147 MLNumTypeSingle.__init__(
148 self, numpy.float64, 'float64', 'double', 'float64')
151class MLNumTypeInt32(MLNumTypeSingle):
152 """
153 A numpy.int32.
154 """
156 def __init__(self):
157 MLNumTypeSingle.__init__(self, numpy.int32, 'int32', 'int', 'int32')
160class MLNumTypeInt64(MLNumTypeSingle):
161 """
162 A numpy.int64.
163 """
165 def __init__(self):
166 MLNumTypeSingle.__init__(
167 self, numpy.int32, 'int64', 'int64_t', 'int64')
170class MLNumTypeBool(MLNumTypeSingle):
171 """
172 A numpy.bool.
173 """
175 def __init__(self):
176 MLNumTypeSingle.__init__(self, numpy.bool_, 'BL', 'bool', 'bool')
179class MLTensor(MLType):
180 """
181 Defines a tensor with a dimension and a single type for what it contains.
182 """
184 def __init__(self, element_type, dim):
185 if not isinstance(element_type, MLType):
186 raise TypeError( # pragma: no cover
187 'element_type must be of MLType not {0}'.format(type(element_type)))
188 if not isinstance(dim, tuple):
189 raise TypeError( # pragma: no cover
190 'dim must be a tuple.')
191 if len(dim) == 0:
192 raise ValueError( # pragma: no cover
193 "dimension must not be null.")
194 for d in dim:
195 if d == 0:
196 raise ValueError( # pragma: no cover
197 "No dimension can be null.")
198 self.dim = dim
199 self.element_type = element_type
201 @property
202 def CTypeSingle(self):
203 """
204 Returns *ctype*.
205 """
206 return self.element_type.ctype
208 def validate(self, value):
209 """
210 Checks that the value is of this type.
211 """
212 MLType.validate(self, value)
213 if not isinstance(value, numpy.ndarray):
214 raise TypeError( # pragma: no cover
215 "value is not a numpy.array but '{0}'".format(type(value)))
216 if self.dim != value.shape:
217 raise ValueError( # pragma: no cover
218 "Dimensions do not match {0}={1}".format(self.dim, value.shape))
219 rvalue = value.ravel()
220 for i, num in enumerate(rvalue):
221 try:
222 self.element_type.validate(num)
223 except TypeError as e: # pragma: no cover
224 raise TypeError(
225 'Unable to convert an array due to value index {0}: {1}'.format(i, rvalue[i])) from e
226 return value
228 def _byref_c(self):
229 return ""
231 def _format_value_json(self, value, hook=None):
232 if hook is None or 'array' not in hook:
233 return value
234 return hook['array'](value)
236 def _format_value_c(self, value, hook=None):
237 return "{{{0}}}".format(", ".join(self.element_type._format_value_c(x) for x in value))
239 def _export_json(self, hook=None, result_name=None):
240 return '{0}:{1}'.format(self.element_type._export_json(hook=hook), self.dim)
242 def _export_c(self, hook=None, result_name=None):
243 if len(self.dim) != 1:
244 raise NotImplementedError( # pragma: no cover
245 'Only 1D vector implemented.')
246 if hook is None:
247 raise ValueError( # pragma: no cover
248 "hook must contains either 'signature' or 'declare'.")
249 if hook == 'signature':
250 if result_name is None:
251 raise ValueError( # pragma: no cover
252 "result_name must be specified.")
253 return {'code': "{0}[{1}] {2}".format(self.element_type._export_c(hook=hook)['code'],
254 self.dim[0], result_name),
255 'result_name': result_name}
256 elif hook == 'declare':
257 if result_name is None:
258 raise ValueError( # pragma: no cover
259 "result_name must be specified.")
260 dc = self.element_type._export_c(
261 hook=hook, result_name=result_name)
262 return {'code': "{0}[{1}]".format(dc['code'], self.dim[0])}
263 elif hook == 'type':
264 return {'code': "{0}*".format(self.element_type._export_c(hook=hook)['code'])}
265 elif hook == 'typeref':
266 if result_name is None:
267 return {'code': "{0}*".format(self.element_type._export_c(hook='type')['code'])}
268 code = self.element_type._export_c(hook='type')['code']
269 return {'code': "{0}* {1}".format(code, result_name), 'result_name': result_name}
270 else:
271 raise ValueError( # pragma: no cover
272 "hook must contains either 'signature' or 'declare' not '{0}'.".format(hook))
274 def _copy_c(self, src, dest, hook=None):
275 if len(self.dim) != 1:
276 raise NotImplementedError( # pragma: no cover
277 'Only 1D vector implemented.')
278 code = self.element_type._export_c(hook='type')['code']
279 return "memcpy({1}, {0}, {2}*sizeof({3}));".format(src, dest, self.dim[0], code)