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

128 statements  

1""" 

2@file 

3@brief Types definition. 

4""" 

5import numpy 

6from .api_extension import AutoType 

7 

8 

9class MLType(AutoType): 

10 """ 

11 Base class for every type. 

12 """ 

13 

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 

20 

21 def cast(self, value): 

22 """ 

23 Converts *value* into this type. 

24 """ 

25 raise NotImplementedError() # pragma: no cover 

26 

27 

28class MLNumType(MLType): 

29 """ 

30 Base class for numerical types. 

31 """ 

32 

33 def _format_value_json(self, value, hook=None): 

34 return str(value) 

35 

36 def _format_value_c(self, value, hook=None): 

37 return str(value) 

38 

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) 

43 

44 

45class MLNumTypeSingle(MLNumType): 

46 """ 

47 int32 or float32 

48 """ 

49 

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 

55 

56 @property 

57 def CTypeSingle(self): 

58 """ 

59 Returns *ctype*. 

60 """ 

61 return self.ctype 

62 

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 

73 

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))) 

88 

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) 

101 

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} 

108 

109 def _byref_c(self): 

110 return "&" 

111 

112 def _export_json(self, hook=None, result_name=None): 

113 return 'float32' 

114 

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) 

119 

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) 

124 

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) 

129 

130 

131class MLNumTypeFloat32(MLNumTypeSingle): 

132 """ 

133 A numpy.float32. 

134 """ 

135 

136 def __init__(self): 

137 MLNumTypeSingle.__init__( 

138 self, numpy.float32, 'float32', 'float', 'float32') 

139 

140 

141class MLNumTypeFloat64(MLNumTypeSingle): 

142 """ 

143 A numpy.float64. 

144 """ 

145 

146 def __init__(self): 

147 MLNumTypeSingle.__init__( 

148 self, numpy.float64, 'float64', 'double', 'float64') 

149 

150 

151class MLNumTypeInt32(MLNumTypeSingle): 

152 """ 

153 A numpy.int32. 

154 """ 

155 

156 def __init__(self): 

157 MLNumTypeSingle.__init__(self, numpy.int32, 'int32', 'int', 'int32') 

158 

159 

160class MLNumTypeInt64(MLNumTypeSingle): 

161 """ 

162 A numpy.int64. 

163 """ 

164 

165 def __init__(self): 

166 MLNumTypeSingle.__init__( 

167 self, numpy.int32, 'int64', 'int64_t', 'int64') 

168 

169 

170class MLNumTypeBool(MLNumTypeSingle): 

171 """ 

172 A numpy.bool. 

173 """ 

174 

175 def __init__(self): 

176 MLNumTypeSingle.__init__(self, numpy.bool_, 'BL', 'bool', 'bool') 

177 

178 

179class MLTensor(MLType): 

180 """ 

181 Defines a tensor with a dimension and a single type for what it contains. 

182 """ 

183 

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 

200 

201 @property 

202 def CTypeSingle(self): 

203 """ 

204 Returns *ctype*. 

205 """ 

206 return self.element_type.ctype 

207 

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 

227 

228 def _byref_c(self): 

229 return "" 

230 

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) 

235 

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)) 

238 

239 def _export_json(self, hook=None, result_name=None): 

240 return '{0}:{1}'.format(self.element_type._export_json(hook=hook), self.dim) 

241 

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)) 

273 

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)