ImpreciseNumber   A
last analyzed

Complexity

Total Complexity 26

Size/Duplication

Total Lines 379
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 379
rs 10
wmc 26

21 Methods

Rating   Name   Duplication   Size   Complexity  
A lower() 0 13 1
A issuperset() 0 16 1
A __and__() 0 17 1
A __gt__() 0 17 1
A __ne__() 0 17 1
A __le__() 0 17 1
A __ge__() 0 17 1
B __init__() 0 27 4
A default_upper() 0 14 1
A __str__() 0 12 1
A intersection() 0 20 2
A __lt__() 0 17 1
A __or__() 0 17 1
A __repr__() 0 13 1
A isdisjoint() 0 19 1
A issubset() 0 16 1
A __eq__() 0 17 1
A union() 0 20 2
A convert() 0 19 1
A default_lower() 0 14 1
A upper() 0 13 1
1
"""
2
This module one type useful in :class:`Context` values:
3
4
* :class:`ImpreciseFloat` for representing imprecise float as intervals.
5
* :class:`ImpreciseInteger` for representing imprecise int as intervals.
6
"""
7
8
import math
9
import sys
10
from abc import abstractmethod
11
from typing import TypeVar, Generic
12
13
N = TypeVar('N', float, int)
14
15
16
class ImpreciseNumber(Generic[N]):
17
    """
18
    The :class:`ImpreciseNumber` class ca be used to represent intervals.
19
20
    .. versionadded:: 0.0.3
21
    """
22
23
    @classmethod
24
    @abstractmethod
25
    def default_lower(cls):
26
        """
27
        Get the default lower limit for that class.
28
29
        Returns
30
        -------
31
            :class:`N`
32
                The default lower limit
33
34
        .. versionadded:: 0.0.3
35
        """
36
        raise NotImplementedError
37
38
    @classmethod
39
    @abstractmethod
40
    def default_upper(cls):
41
        """
42
        Get the default upper limit for that class.
43
44
        Returns
45
        -------
46
            :class:`N`
47
                The default upper limit
48
49
        .. versionadded:: 0.0.3
50
        """
51
        raise NotImplementedError
52
53
    @classmethod
54
    @abstractmethod
55
    def convert(cls, value):
56
        """
57
        Convert the value to the desired type.
58
59
        Parameters
60
        ----------
61
            value
62
                Value to be converted
63
64
        Returns
65
        -------
66
            :class:`N`
67
                The default lower limit
68
69
        .. versionadded:: 0.0.3
70
        """
71
        raise NotImplementedError
72
73
    def __init__(self, **kwargs):
74
        """
75
        Initialise an :class:`ImpreciseNumber`.
76
77
        Keyword arguments
78
        -----------------
79
            lower : :class:`N`
80
                the lower limit of the interval
81
            upper : :class:`N`
82
                the upper limit of the interval
83
84
        .. versionadded:: 0.0.2
85
        """
86
87
        if 'lower' in kwargs:
88
            self._lower = self.convert(kwargs['lower'])
89
        else:
90
            self._lower = self.default_lower()
91
92
        if 'upper' in kwargs:
93
            self._upper = self.convert(kwargs['upper'])
94
        else:
95
            self._upper = self.default_upper()
96
97
        if self._lower > self._upper:
98
            self._lower = self.default_upper()
99
            self._upper = self.default_lower()
100
101
    @property
102
    def lower(self):
103
        """
104
        Get the lower limit.
105
106
        Returns
107
        -------
108
            :class:`N`
109
                the lower limit
110
111
        .. versionadded:: 0.0.2
112
        """
113
        return self._lower
114
115
    @property
116
    def upper(self):
117
        """
118
        Get the upper limit.
119
120
        Returns
121
        -------
122
            :class:`N`
123
                the upper limit
124
125
        .. versionadded:: 0.0.2
126
        """
127
        return self._upper
128
129
    def __le__(self, other: 'ImpreciseNumber[N]') -> bool:
130
        """
131
        Test if the imprecise number is included (or equal) to the other.
132
133
        Parameters
134
        ----------
135
            other : :class:`ImpreciseNumber[N]`
136
                the other imprecise number
137
138
        Returns
139
        -------
140
            :class:`bool`
141
                True if the imprecise number is included in the other
142
143
        .. versionadded:: 0.0.2
144
        """
145
        return self._lower >= other.lower and self._upper <= other.upper
146
147
    def __lt__(self, other: 'ImpreciseNumber[N]') -> bool:
148
        """
149
        Test if the imprecise number is strictly included to the other.
150
151
        Parameters
152
        ----------
153
            other : :class:`ImpreciseNumber[N]`
154
                the other imprecise number
155
156
        Returns
157
        -------
158
            :class:`bool`
159
                True if the imprecise number is strictly included in the other
160
161
        .. versionadded:: 0.0.2
162
        """
163
        return self <= other and self != other
164
165
    def __eq__(self, other: 'ImpreciseNumber[N]') -> bool:
166
        """
167
        Test if the imprecise number is equal to the other.
168
169
        Parameters
170
        ----------
171
            other : :class:`ImpreciseNumber[N]`
172
                the other imprecise number
173
174
        Returns
175
        -------
176
            :class:`bool`
177
                True if the imprecise number is equal to the other
178
179
        .. versionadded:: 0.0.2
180
        """
181
        return self._lower == other.lower and self._upper == other.upper
182
183
    def __ne__(self, other: 'ImpreciseNumber[N]') -> bool:
184
        """
185
        Test if the imprecise number is not equal to the other.
186
187
        Parameters
188
        ----------
189
            other : :class:`ImpreciseNumber[N]`
190
                the other imprecise number
191
192
        Returns
193
        -------
194
            :class:`bool`
195
                True if the imprecise number is not equal to the other
196
197
        .. versionadded:: 0.0.2
198
        """
199
        return not self == other
200
201
    def __ge__(self, other: N) -> bool:
202
        """
203
        Test if the imprecise number includes or is equal to the other.
204
205
        Parameters
206
        ----------
207
            other : :class:`N`
208
                the other imprecise number
209
210
        Returns
211
        -------
212
            :class:`bool`
213
                True if the imprecise number includes or is equal to the other
214
215
        .. versionadded:: 0.0.2
216
        """
217
        return self._lower <= other.lower and self._upper >= other.upper
218
219
    def __gt__(self, other: N) -> bool:
220
        """
221
        Test if the imprecise number strictly includes the other.
222
223
        Parameters
224
        ----------
225
            other : :class:`N`
226
                the other imprecise number
227
228
        Returns
229
        -------
230
            :class:`bool`
231
                True if the imprecise number strictly includes the other
232
233
        .. versionadded:: 0.0.2
234
        """
235
        return self >= other and self != other
236
237
    def __and__(self, other: 'ImpreciseNumber[N]') -> 'ImpreciseNumber[N]':
238
        """
239
        Compute the intersection of the the imprecise number with the other.
240
241
        Parameters
242
        ----------
243
            other : :class:`ImpreciseNumber[N]`
244
                the other imprecise number
245
246
        Returns
247
        -------
248
            :class:`ImpreciseNumber[N]`
249
                the intersection of the the imprecise number with the other
250
251
        .. versionadded:: 0.0.2
252
        """
253
        return type(self)(lower=max(self._lower, other.lower), upper=min(self._upper, other.upper))
254
255
    def __or__(self, other: 'ImpreciseNumber[N]') -> 'ImpreciseNumber[N]':
256
        """
257
        Compute the extension of the the imprecise number with the other.
258
259
        Parameters
260
        ----------
261
            other : :class:`ImpreciseNumber[N]`
262
                the other imprecise number
263
264
        Returns
265
        -------
266
            :class:`ImpreciseNumber[N]`
267
                the extension of the the imprecise number with the other
268
269
        .. versionadded:: 0.0.2
270
        """
271
        return type(self)(lower=min(self._lower, other.lower), upper=max(self._upper, other.upper))
272
273
    def isdisjoint(self, other: 'ImpreciseNumber[N]') -> bool:
274
        """
275
        Return True if the imprecise number has no elements in common with the other.
276
        Imprecise numbers are disjoint if and only if their intersection is the empty
277
        imprecise number.
278
279
        Parameters
280
        ----------
281
            other : :class:`ImpreciseNumber[N]`
282
                the other imprecise number
283
284
        Returns
285
        -------
286
            :class:`bool`
287
                True if the imprecise number is disjoint from the other
288
289
        .. versionadded:: 0.0.2
290
        """
291
        return self._upper < other.lower or self._lower > other.upper
292
293
    def issubset(self, other: 'ImpreciseNumber[N]') -> bool:
294
        """
295
        Test if the imprecise number is included (or equal) to the other.
296
297
        Parameters
298
        ----------
299
            other : :class:`ImpreciseNumber[N]`
300
301
        Returns
302
        -------
303
            :class:`bool`
304
                True if this imprecise number is included or equal to the other
305
306
        .. versionadded:: 0.0.2
307
        """
308
        return self <= other
309
310
    def issuperset(self, other: 'ImpreciseNumber[N]') -> bool:
311
        """
312
        Test if the imprecise number includes (or is equal to) the other.
313
314
        Parameters
315
        ----------
316
            other : :class:`ImpreciseNumber[N]`
317
318
        Returns
319
        -------
320
            :class:`bool`
321
                True if this imprecise number includes (or is equal to) the other
322
323
        .. versionadded:: 0.0.2
324
        """
325
        return self >= other
326
327
    def union(self, *others) -> 'ImpreciseNumber[N]':
328
        """
329
        Compute the union between this imprecise number and the others.
330
331
        Parameters
332
        ----------
333
            *others
334
                Variable length argument list
335
336
        Returns
337
        -------
338
            :class:`ImpreciseNumber[N]`
339
                the union between this imprecise number and the others
340
341
        .. versionadded:: 0.0.2
342
        """
343
        result = self
344
        for other in others:
345
            result = result | other
346
        return result
347
348
    def intersection(self, *others) -> 'ImpreciseNumber[N]':
349
        """
350
        Compute the intersection between this imprecise number and the others.
351
352
        Parameters
353
        ----------
354
            *others
355
                Variable length argument list
356
357
        Returns
358
        -------
359
            :class:`ImpreciseNumber[N]`
360
                the intersection between this imprecise number and the others
361
362
        .. versionadded:: 0.0.2
363
        """
364
        result = self
365
        for other in others:
366
            result = result & other
367
        return result
368
369
    def __repr__(self) -> str:
370
        """
371
        Convert this imprecise number to a representable string.
372
373
        Returns
374
        -------
375
            :class:`str`
376
                the user friendly representable string of this imprecise number
377
378
        .. versionadded:: 0.0.2
379
        """
380
        return '%s(lower=%s, upper=%s)' %\
381
               (type(self).__name__, repr(self._lower), repr(self._upper))
382
383
    def __str__(self) -> str:
384
        """
385
        Convert this imprecise number to a representable string.
386
387
        Returns
388
        -------
389
            :class:`str`
390
                the user friendly representable string of this imprecise number
391
392
        .. versionadded:: 0.0.2
393
        """
394
        return '[%s:%s]' % (repr(self._lower), repr(self._upper))
395
396
397
class ImpreciseFloat(ImpreciseNumber[float]):
398
    """
399
    :class:`ImpreciseFloat` instances represents subset of the real line.
400
401
    .. versionadded:: 0.0.2
402
    """
403
404
    @classmethod
405
    def convert(cls, value):
406
        """
407
        Convert the value to a float.
408
409
        Parameters
410
        ----------
411
            value
412
                The value to convert
413
414
        Returns
415
        -------
416
            :class:`float`
417
                The value converted
418
419
        .. versionadded:: 0.0.3
420
        """
421
        return float(value)
422
423
    @classmethod
424
    def default_lower(cls):
425
        """
426
        Get the default lower limit for that class.
427
428
        Returns
429
        -------
430
            :class:`float`
431
                The default lower limit is fixed to :code:`-math.inf`
432
433
        .. versionadded:: 0.0.3
434
        """
435
        return -math.inf
436
437
    @classmethod
438
    def default_upper(cls):
439
        """
440
        Get the default upper limit for that class.
441
442
        Returns
443
        -------
444
            :class:`float`
445
                The default upper limit is fixed to :code:`math.inf`
446
447
        .. versionadded:: 0.0.3
448
        """
449
        return math.inf
450
451
452
class ImpreciseInteger(ImpreciseNumber[int]):
453
    """
454
    :class:`ImpreciseInteger` instances represents subset of the integer set.
455
456
    .. versionadded:: 0.0.3
457
    """
458
459
    @classmethod
460
    def convert(cls, value):
461
        """
462
        Convert the value to an int.
463
464
        Parameters
465
        ----------
466
            value
467
                The value to convert
468
469
        Returns
470
        -------
471
            :class:`int`
472
                The value converted
473
474
        .. versionadded:: 0.0.3
475
        """
476
        return int(value)
477
478
    @classmethod
479
    def default_lower(cls):
480
        """
481
        Get the default lower limit for that class.
482
483
        Returns
484
        -------
485
            :class:`int`
486
                The default lower limit is fixed to :code:`-sys.maxsize`
487
488
        .. versionadded:: 0.0.3
489
        """
490
        return -sys.maxsize
491
492
    @classmethod
493
    def default_upper(cls):
494
        """
495
        Get the default upper limit for that class.
496
497
        Returns
498
        -------
499
            :class:`int`
500
                The default upper limit is fixed to :code:`sys.maxsize`
501
502
        .. versionadded:: 0.0.3
503
        """
504
        return sys.maxsize
505