| 305 | |
| 306 | |
| 307 | class ArrayStrategy(st.SearchStrategy): |
| 308 | def __init__( |
| 309 | self, *, xp, api_version, elements_strategy, dtype, shape, fill, unique |
| 310 | ): |
| 311 | super().__init__() |
| 312 | self.xp = xp |
| 313 | self.elements_strategy = elements_strategy |
| 314 | self.dtype = dtype |
| 315 | self.shape = shape |
| 316 | self.fill = fill |
| 317 | self.unique = unique |
| 318 | self.array_size = math.prod(shape) |
| 319 | self.builtin = find_castable_builtin_for_dtype(xp, api_version, dtype) |
| 320 | self.finfo = None if self.builtin is not float else xp.finfo(self.dtype) |
| 321 | |
| 322 | def check_set_value(self, val, val_0d, strategy): |
| 323 | if val == val and self.builtin(val_0d) != val: |
| 324 | if self.builtin is float: |
| 325 | assert self.finfo is not None # for mypy |
| 326 | try: |
| 327 | is_subnormal = 0 < abs(val) < self.finfo.smallest_normal |
| 328 | except Exception: |
| 329 | # val may be a non-float that does not support the |
| 330 | # operations __lt__ and __abs__ |
| 331 | is_subnormal = False |
| 332 | if is_subnormal: |
| 333 | raise InvalidArgument( |
| 334 | f"Generated subnormal float {val} from strategy " |
| 335 | f"{strategy} resulted in {val_0d!r}, probably " |
| 336 | f"as a result of array module {self.xp.__name__} " |
| 337 | "being built with flush-to-zero compiler options. " |
| 338 | "Consider passing allow_subnormal=False." |
| 339 | ) |
| 340 | raise InvalidArgument( |
| 341 | f"Generated array element {val!r} from strategy {strategy} " |
| 342 | f"cannot be represented with dtype {self.dtype}. " |
| 343 | f"Array module {self.xp.__name__} instead " |
| 344 | f"represents the element as {val_0d}. " |
| 345 | "Consider using a more precise elements strategy, " |
| 346 | "for example passing the width argument to floats()." |
| 347 | ) |
| 348 | |
| 349 | def do_draw(self, data): |
| 350 | if 0 in self.shape: |
| 351 | return self.xp.zeros(self.shape, dtype=self.dtype) |
| 352 | |
| 353 | if self.fill.is_empty: |
| 354 | # We have no fill value (either because the user explicitly |
| 355 | # disabled it or because the default behaviour was used and our |
| 356 | # elements strategy does not produce reusable values), so we must |
| 357 | # generate a fully dense array with a freshly drawn value for each |
| 358 | # entry. |
| 359 | elems = data.draw( |
| 360 | st.lists( |
| 361 | self.elements_strategy, |
| 362 | min_size=self.array_size, |
| 363 | max_size=self.array_size, |
| 364 | unique=self.unique, |
no outgoing calls