Coverage for src / mppy / scalar.py: 100%

55 statements  

« prev     ^ index     » next       coverage.py v7.14.0, created at 2026-05-13 09:54 +0200

1from functools import total_ordering 

2 

3import numpy as np 

4 

5from .constants import eps,e 

6 

7""" 

8Class used to represent the scalar. 

9It used np.float64 to store the value, but does not inherit from np.float64 

10""" 

11 

12 

13@total_ordering 

14class Scalar: 

15 def __init__(self, other): 

16 self.__value: np.float64 = np.float64(other) 

17 

18 @classmethod 

19 def eps(cls) -> Scalar: 

20 """ 

21 Directly returns a scalar with -np.inf as value 

22 """ 

23 return Scalar(eps) 

24 

25 @classmethod 

26 def e(cls) -> Scalar: 

27 """ 

28 Directly returns a scalar with 0 as value 

29 """ 

30 return Scalar(e) 

31 

32 def val(self) -> np.float64: 

33 return self.__value 

34 

35 def _extract_value(self, other) -> np.float64: 

36 """ 

37 Extracts value from other, if other is a Scalar then other.__value is used, otherwise other is 

38 casted to np.float64 

39 """ 

40 return other.__value if isinstance(other, Scalar) else np.float64(other) 

41 

42 def __add__(self, other) -> Scalar: 

43 """ 

44 Returns the maximum of self and other 

45 """ 

46 if isinstance(other, Scalar) or isinstance(other, (int, float, np.number)): 

47 val = self._extract_value(other) 

48 return Scalar(max(self.val(), val)) 

49 return NotImplemented 

50 

51 def __radd__(self, other) -> Scalar: 

52 """ 

53 Returns the maximum of self and other 

54 """ 

55 return self.__add__(other) 

56 

57 def __mul__(self, other) -> Scalar: 

58 """ 

59 Returns the addition of self and other 

60 """ 

61 # we need this check so scalar * matrix would not use this __mul__ but the __mul__ of the matrix 

62 if isinstance(other, Scalar) or isinstance(other, (int, float, np.number)): 

63 val = self._extract_value(other) 

64 return Scalar(self.val() + val) 

65 return NotImplemented 

66 

67 def __rmul__(self, other) -> Scalar: 

68 """ 

69 Returns the addition of self and other 

70 """ 

71 return self.__mul__(other) 

72 

73 def __pow__(self, other) -> Scalar: 

74 """ 

75 Returns the product a * n for a^n 

76 """ 

77 if not isinstance(other, (Scalar, np.number, int, float)): 

78 raise ValueError("Exponent needs to be scalar or number") 

79 exp = self._extract_value(other) 

80 if exp == 0: 

81 return Scalar.e() 

82 return Scalar(self.val() * exp) 

83 

84 def __neg__(self) -> Scalar: 

85 """ 

86 Negates the value of the scalar 

87 """ 

88 return Scalar(-self.val()) 

89 

90 # we only need __lt__ and __eq__, because the class is @total_ordering and any other ordering 

91 # can be infered 

92 def __lt__(self, other) -> bool: 

93 if not isinstance(other, (Scalar, np.number, int, float)): 

94 raise ValueError("Scalar can only be compared to other scalars or numbers") 

95 val = self._extract_value(other) 

96 return self.val() < val 

97 

98 def __eq__(self, other) -> bool: 

99 if not isinstance(other, (Scalar, np.number, int, float)): 

100 return False 

101 val = self._extract_value(other) 

102 return self.val() == val 

103 

104 def __str__(self) -> str: 

105 if self.val() == -np.inf: 

106 return "ε" 

107 else: 

108 return f"{self.val()}"