Coverage for nodimo / matrix.py: 100%
113 statements
« prev ^ index » next coverage.py v7.13.4, created at 2026-02-26 03:10 +0000
« 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
5"""
6Matrix
7======
9This module contains the class to create a dimensional matrix.
11Classes
12-------
13DimensionalMatrix
14 Creates a dimensional matrix from a group of quantities.
15"""
17from sympy import Symbol, ImmutableDenseMatrix, Matrix, S 1abcdef
18from sympy.printing.pretty.stringpict import prettyForm 1abcdef
20from nodimo.quantity import Quantity 1abcdef
21from nodimo.groups import Group 1abcdef
24class DimensionalMatrix(Group): 1abcdef
25 """Dimensional matrix of a group of quantities.
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.
33 Parameters
34 ----------
35 *quantities : Quantity
36 Quantities that constitute the dimensional matrix.
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.
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.
56 Warns
57 -----
58 NodimoWarning
59 Dimensions that are treated as independent.
61 Examples
62 --------
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 """
72 def __init__(self, *quantities: Quantity): 1abcdef
73 super().__init__(*quantities) 1abcdef
74 self._set_dimensional_matrix() 1abcdef
76 @property 1abcdef
77 def matrix(self) -> ImmutableDenseMatrix: 1abcdef
78 return self._matrix 1abcdef
80 @property 1abcdef
81 def rank(self) -> int: 1abcdef
82 return self._rank 1abcdef
84 @property 1abcdef
85 def independent_rows(self) -> tuple[int]: 1abcdef
86 return self._independent_rows 1abcdef
88 def set_dimensions_order(self, *dimensions_names: str): 1abcdef
89 """Sets the dimensions column order.
91 Parameters
92 ----------
93 *dimensions_names : str
94 Dimensions' names in the requested order.
95 """
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
101 self._set_dimensions(**dimensions) 1abcdef
102 self._set_matrix() 1abcdef
103 self._set_symbolic_dimensional_matrix() 1abcdef
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
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
118 self._symbolic = labeled_matrix.as_immutable() 1abcdef
120 def _sympy_(self): 1abcdef
121 return self._matrix 1abcdef
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
127 return f'{class_name}({quantities})' 1abcdef
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
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
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
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
160 return dmatrix 1abcdef
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
178 return dmatrix 1abcdef
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
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
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
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
211 if i == 0: 1abcdef
212 dmatrix = row 1abcdef
213 else:
214 dmatrix = prettyForm(*dmatrix.below(row)) 1abcdef
216 return dmatrix 1abcdef