Coverage for nodimo / matrix.py: 100%

113 statements  

« prev     ^ index     » next       coverage.py v7.13.4, created at 2026-02-26 03:10 +0000

1# ┓• Licensed under the MIT License 

2# ┏┓┏┓┏┫┓┏┳┓┏┓ Copyright (c) 2024 Rodrigo Castro 

3# ┛┗┗┛┗┻┗┛┗┗┗┛ https://nodimo.readthedocs.io 

4 

5""" 

6Matrix 

7====== 

8 

9This module contains the class to create a dimensional matrix. 

10 

11Classes 

12------- 

13DimensionalMatrix 

14 Creates a dimensional matrix from a group of quantities. 

15""" 

16 

17from sympy import Symbol, ImmutableDenseMatrix, Matrix, S 1abcdef

18from sympy.printing.pretty.stringpict import prettyForm 1abcdef

19 

20from nodimo.quantity import Quantity 1abcdef

21from nodimo.groups import Group 1abcdef

22 

23 

24class DimensionalMatrix(Group): 1abcdef

25 """Dimensional matrix of a group of quantities. 

26 

27 A DimensionalMatrix is a matrix with one column for each quantity, 

28 one row for each dimension's name, and every element represents the 

29 dimension's exponent of a particular quantity. Labels at the top 

30 and at the right side are added to identify the quantities and the 

31 dimensions' names, respectively. 

32 

33 Parameters 

34 ---------- 

35 *quantities : Quantity 

36 Quantities that constitute the dimensional matrix. 

37 

38 Attributes 

39 ---------- 

40 quantities : list[Quantity] 

41 List with the quantities used to build the dimensional matrix. 

42 matrix : ImmutableDenseMatrix 

43 Matrix containing only the dimensions' exponents. 

44 rank : int 

45 The rank of the dimensional matrix. 

46 independent_rows : tuple[int] 

47 Indexes of the dimensional matrix independent rows. 

48 

49 Methods 

50 ------- 

51 set_dimensions_order(*dimensions_names) 

52 Sets the dimensions' names order. 

53 show(use_custom_css=True, use_unicode=True) 

54 Displays the dimensional matrix. 

55 

56 Warns 

57 ----- 

58 NodimoWarning 

59 Dimensions that are treated as independent. 

60 

61 Examples 

62 -------- 

63 

64 >>> from nodimo import Quantity, DimensionalMatrix 

65 >>> F = Quantity('F', M=1, L=1, T=-2) 

66 >>> k = Quantity('k', M=1, T=-2) 

67 >>> x = Quantity('x', L=1) 

68 >>> dmatrix = DimensionalMatrix(F, k, x, k*x) 

69 >>> dmatrix.show() 

70 """ 

71 

72 def __init__(self, *quantities: Quantity): 1abcdef

73 super().__init__(*quantities) 1abcdef

74 self._set_dimensional_matrix() 1abcdef

75 

76 @property 1abcdef

77 def matrix(self) -> ImmutableDenseMatrix: 1abcdef

78 return self._matrix 1abcdef

79 

80 @property 1abcdef

81 def rank(self) -> int: 1abcdef

82 return self._rank 1abcdef

83 

84 @property 1abcdef

85 def independent_rows(self) -> tuple[int]: 1abcdef

86 return self._independent_rows 1abcdef

87 

88 def set_dimensions_order(self, *dimensions_names: str): 1abcdef

89 """Sets the dimensions column order. 

90 

91 Parameters 

92 ---------- 

93 *dimensions_names : str 

94 Dimensions' names in the requested order. 

95 """ 

96 

97 dimensions = dict((dim, S.Zero) for dim in dimensions_names) 1abcdef

98 for dim in self._dimensions: 1abcdef

99 dimensions[dim] = self._dimensions[dim] 1abcdef

100 

101 self._set_dimensions(**dimensions) 1abcdef

102 self._set_matrix() 1abcdef

103 self._set_symbolic_dimensional_matrix() 1abcdef

104 

105 def _set_dimensional_matrix(self): 1abcdef

106 self._set_matrix() 1abcdef

107 self._set_matrix_rank() 1abcdef

108 self._set_matrix_independent_rows() 1abcdef

109 self._set_symbolic_dimensional_matrix() 1abcdef

110 

111 def _set_symbolic_dimensional_matrix(self): 1abcdef

112 labeled_matrix = self._matrix.as_mutable() 1abcdef

113 dimensions_column = Matrix(list(self._dimensions)) 1abcdef

114 quantities_row = Matrix([[Symbol('')] + list(self._quantities)]) 1abcdef

115 labeled_matrix = labeled_matrix.col_insert(0, dimensions_column) 1abcdef

116 labeled_matrix = labeled_matrix.row_insert(0, quantities_row) 1abcdef

117 

118 self._symbolic = labeled_matrix.as_immutable() 1abcdef

119 

120 def _sympy_(self): 1abcdef

121 return self._matrix 1abcdef

122 

123 def _sympyrepr(self, printer) -> str: 1abcdef

124 class_name = type(self).__name__ 1abcdef

125 quantities = ', '.join(printer._print(qty) for qty in self._quantities) 1abcdef

126 

127 return f'{class_name}({quantities})' 1abcdef

128 

129 def _sympystr(self, printer) -> str: 1abcdef

130 nrows = len(self._dimensions) + 1 1abcdef

131 ncols = len(self._quantities) + 1 1abcdef

132 raw_dmatrix = {} 1abcdef

133 for i in range(nrows): 1abcdef

134 for j in range(ncols): 1abcdef

135 if i == j == 0: 1abcdef

136 raw_dmatrix[i, j] = printer._print('') 1abcdef

137 elif i == 0: 1abcdef

138 raw_dmatrix[i, j] = printer._print(self._quantities[j - 1]) 1abcdef

139 elif j == 0: 1abcdef

140 raw_dmatrix[i, j] = printer._print(list(self._dimensions)[i - 1]) 1abcdef

141 else: 

142 raw_dmatrix[i, j] = printer._print(self._matrix[i - 1, j - 1]) 1abcdef

143 

144 maxwidth = [] 1abcdef

145 for j in range(ncols): 1abcdef

146 maxwidth.append(max(len(raw_dmatrix[i, j]) for i in range(nrows))) 1abcdef

147 

148 for i in range(nrows): 1abcdef

149 for j in range(ncols): 1abcdef

150 nspaces = maxwidth[j] - len(raw_dmatrix[i, j]) 1abcdef

151 left_space = ' ' * nspaces 1abcdef

152 raw_dmatrix[i, j] = left_space + raw_dmatrix[i, j] 1abcdef

153 

154 rows = [] 1abcdef

155 for i in range(nrows): 1abcdef

156 row = ' '.join(raw_dmatrix[i, j] for j in range(ncols)) 1abcdef

157 rows.append(row) 1abcdef

158 dmatrix = '\n'.join(rows) 1abcdef

159 

160 return dmatrix 1abcdef

161 

162 def _latex(self, printer) -> str: 1abcdef

163 dmatrix = R'\begin{array}' 1abcdef

164 dmatrix += '{r|' + 'r' * len(self._quantities) + '} & ' 1abcdef

165 dmatrix += ' & '.join([printer._print(qty) for qty in self._quantities]) 1abcdef

166 dmatrix += R' \\ \hline ' 1abcdef

167 for dim, exponents in zip(self._dimensions, self._raw_matrix): 1abcdef

168 row = [f'\\mathsf{{{dim}}}'] 1abcdef

169 for exp in exponents: 1abcdef

170 if exp < 0: 1abcdef

171 row.append(printer._print(exp)) 1abcdef

172 else: 

173 # Mimic the minus sign to preserve column width. 

174 row.append(R'\phantom{-}' + printer._print(exp)) 1abcdef

175 dmatrix += ' & '.join(row) + R' \\ ' 1abcdef

176 dmatrix += R'\end{array}' 1abcdef

177 

178 return dmatrix 1abcdef

179 

180 def _pretty(self, printer) -> prettyForm: 1abcdef

181 nrows = len(self._dimensions) + 1 1abcdef

182 ncols = len(self._quantities) + 1 1abcdef

183 raw_dmatrix = {} 1abcdef

184 for i in range(nrows): 1abcdef

185 for j in range(ncols): 1abcdef

186 if i == j == 0: 1abcdef

187 raw_dmatrix[i, j] = printer._print('') 1abcdef

188 elif i == 0: 1abcdef

189 raw_dmatrix[i, j] = printer._print(self._quantities[j - 1]) 1abcdef

190 elif j == 0: 1abcdef

191 raw_dmatrix[i, j] = printer._print(list(self._dimensions)[i - 1]) 1abcdef

192 else: 

193 raw_dmatrix[i, j] = printer._print(self._matrix[i - 1, j - 1]) 1abcdef

194 

195 maxwidth = [] 1abcdef

196 for j in range(ncols): 1abcdef

197 maxwidth.append(max(raw_dmatrix[i, j].width() for i in range(nrows))) 1abcdef

198 

199 for i in range(nrows): 1abcdef

200 for j in range(ncols): 1abcdef

201 nspaces = 0 if j == 0 else 2 1abcdef

202 nspaces += maxwidth[j] - raw_dmatrix[i, j].width() 1abcdef

203 left_space = prettyForm(' ' * nspaces) 1abcdef

204 raw_dmatrix[i, j] = prettyForm(*left_space.right(raw_dmatrix[i, j])) 1abcdef

205 

206 for i in range(nrows): 1abcdef

207 row = raw_dmatrix[i, 0] 1abcdef

208 for j in range(1, ncols): 1abcdef

209 row = prettyForm(*row.right(raw_dmatrix[i, j])) 1abcdef

210 

211 if i == 0: 1abcdef

212 dmatrix = row 1abcdef

213 else: 

214 dmatrix = prettyForm(*dmatrix.below(row)) 1abcdef

215 

216 return dmatrix 1abcdef