Coverage for physioblocks / base / operators.py: 98%

82 statements  

« prev     ^ index     » next       coverage.py v7.13.1, created at 2026-01-09 16:40 +0100

1# SPDX-FileCopyrightText: Copyright INRIA 

2# 

3# SPDX-License-Identifier: LGPL-3.0-only 

4# 

5# Copyright INRIA 

6# 

7# This file is part of PhysioBlocks, a library mostly developed by the 

8# [Ananke project-team](https://team.inria.fr/ananke) at INRIA. 

9# 

10# Authors: 

11# - Colin Drieu 

12# - Dominique Chapelle 

13# - François Kimmig 

14# - Philippe Moireau 

15# 

16# PhysioBlocks is free software: you can redistribute it and/or modify it under the 

17# terms of the GNU Lesser General Public License as published by the Free Software 

18# Foundation, version 3 of the License. 

19# 

20# PhysioBlocks is distributed in the hope that it will be useful, but WITHOUT ANY 

21# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A 

22# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. 

23# 

24# You should have received a copy of the GNU Lesser General Public License along with 

25# PhysioBlocks. If not, see <https://www.gnu.org/licenses/>. 

26 

27"""Define base classe to provide generic base operators""" 

28 

29from abc import ABC, abstractmethod 

30from typing import Any 

31 

32import numpy as np 

33from numpy.typing import NDArray 

34 

35 

36class AbstractBaseOperators(ABC): 

37 """Base class defining behaviors for common operators 

38 

39 The derived classes should implement the :attr:`operation_value` property 

40 to return the attribute of the class that will be used with base 

41 operators. 

42 """ 

43 

44 @property 

45 @abstractmethod 

46 def operation_value(self) -> Any: 

47 """ 

48 Overwrite in derived class to return the value used by the 

49 class for the base operators. 

50 """ 

51 raise NotImplementedError( 

52 str.format( 

53 "{0} is not implemented in {1} class.", 

54 AbstractBaseOperators.operation_value.__name__, 

55 type(self), 

56 ) 

57 ) 

58 

59 # Convertion 

60 

61 def __float__(self) -> float: 

62 return float(self.operation_value) 

63 

64 def __array__( 

65 self, dtype: type | None = None, copy: bool | None = None 

66 ) -> NDArray[Any]: 

67 return np.asarray(self.operation_value, dtype=dtype, copy=copy) 

68 

69 # Compare 

70 

71 # Equality 

72 def __eq__(self, other: Any) -> bool: 

73 compared_value = ( 

74 other.operation_value if isinstance(other, type(self)) else other 

75 ) 

76 return bool(self.operation_value == compared_value) 

77 

78 # Greater 

79 def __ge__(self, other: Any) -> bool: 

80 if isinstance(other, type(self)): 

81 return bool(self.operation_value >= other.operation_value) 

82 return bool(self.operation_value >= other) 

83 

84 def __gt__(self, other: Any) -> bool: 

85 if isinstance(other, type(self)): 

86 return bool(self.operation_value > other.operation_value) 

87 return bool(self.operation_value > other) 

88 

89 # Lesser 

90 def __le__(self, other: Any) -> bool: 

91 if isinstance(other, type(self)): 

92 return bool(self.operation_value <= other.operation_value) 

93 return bool(self.operation_value <= other) 

94 

95 def __lt__(self, other: Any) -> bool: 

96 if isinstance(other, type(self)): 

97 return bool(self.operation_value < other.operation_value) 

98 return bool(self.operation_value < other) 

99 

100 # Base Operations 

101 

102 # Sum 

103 def __add__(self, other: Any) -> Any: 

104 if isinstance(other, type(self)): 

105 return self.operation_value + other.operation_value 

106 return self.operation_value + other 

107 

108 def __radd__(self, other: Any) -> Any: 

109 return other + self.operation_value 

110 

111 # Subtraction 

112 def __sub__(self, other: Any) -> Any: 

113 if isinstance(other, type(self)): 

114 return self.operation_value - other.operation_value 

115 return self.operation_value - other 

116 

117 def __rsub__(self, other: Any) -> Any: 

118 return other - self.operation_value 

119 

120 # Oposite 

121 def __neg__(self) -> Any: 

122 return -self.operation_value 

123 

124 # Multiplication 

125 def __mul__(self, other: Any) -> Any: 

126 if isinstance(other, type(self)): 

127 return self.operation_value * other.operation_value 

128 return self.operation_value * other 

129 

130 def __rmul__(self, other: Any) -> Any: 

131 return other * self.operation_value 

132 

133 # MatMul 

134 def __matmul__(self, other: Any) -> Any: 

135 if isinstance(other, type(self)): 

136 return self.operation_value @ other.operation_value 

137 return self.operation_value @ other 

138 

139 def __rmatmul__(self, other: Any) -> Any: 

140 return other @ self.operation_value 

141 

142 # Division 

143 def __truediv__(self, other: Any) -> Any: 

144 if isinstance(other, type(self)): 

145 return self.operation_value / other.operation_value 

146 return self.operation_value / other 

147 

148 def __rtruediv__(self, other: Any) -> Any: 

149 return other / self.operation_value 

150 

151 # Floor Division 

152 def __floordiv__(self, other: Any) -> Any: 

153 if isinstance(other, type(self)): 

154 return self.operation_value // other.operation_value 

155 return self.operation_value // other 

156 

157 def __rfloordiv__(self, other: Any) -> Any: 

158 return other // self.operation_value 

159 

160 # Modulo 

161 def __mod__(self, other: Any) -> Any: 

162 if isinstance(other, type(self)): 

163 return self.operation_value % other.operation_value 

164 return self.operation_value % other 

165 

166 def __rmod__(self, other: Any) -> Any: 

167 return other % self.operation_value 

168 

169 # Power 

170 def __pow__(self, other: Any) -> Any: 

171 if isinstance(other, type(self)): 

172 return self.operation_value**other.operation_value 

173 return self.operation_value**other 

174 

175 def __rpow__(self, other: Any) -> Any: 

176 return other**self.operation_value