| 148 | 148 | |
| 149 | 149 | @property |
| 150 | 150 | def rowblocksizes(self): |
| 151 | return [self.blocks[i, 0].rows for i in range(self.blockshape[0])] | |
| 151 | from sympy.core.numbers import Zero | |
| 152 | result = [] | |
| 153 | for i in range(self.blockshape[0]): | |
| 154 | block = self.blocks[i, 0] | |
| 155 | if isinstance(block, Zero): | |
| 156 | # For scalar Zero objects, infer dimensions from other blocks in the same row | |
| 157 | # This occurs when ZeroMatrix objects are multiplied and become scalar zeros | |
| 158 | inferred_rows = None | |
| 159 | for j in range(self.blockshape[1]): | |
| 160 | other_block = self.blocks[i, j] | |
| 161 | if not isinstance(other_block, Zero): | |
| 162 | inferred_rows = other_block.rows | |
| 163 | break | |
| 164 | if inferred_rows is not None: | |
| 165 | result.append(inferred_rows) | |
| 166 | else: | |
| 167 | # If all blocks in this row are scalar zeros, we cannot infer dimensions | |
| 168 | raise AttributeError( | |
| 169 | f"Cannot determine row size of scalar Zero at blocks[{i}, 0]. " | |
| 170 | f"All blocks in row {i} are scalar zeros, so dimensions cannot be inferred.") | |
| 171 | else: | |
| 172 | result.append(block.rows) | |
| 173 | return result | |
| 152 | 174 | |
| 153 | 175 | @property |
| 154 | 176 | def colblocksizes(self): |
| 155 | return [self.blocks[0, i].cols for i in range(self.blockshape[1])] | |
| 177 | from sympy.core.numbers import Zero | |
| 178 | result = [] | |
| 179 | for i in range(self.blockshape[1]): | |
| 180 | block = self.blocks[0, i] | |
| 181 | if isinstance(block, Zero): | |
| 182 | # For scalar Zero objects, infer dimensions from other blocks in the same column | |
| 183 | # This occurs when ZeroMatrix objects are multiplied and become scalar zeros | |
| 184 | inferred_cols = None | |
| 185 | for j in range(self.blockshape[0]): | |
| 186 | other_block = self.blocks[j, i] | |
| 187 | if not isinstance(other_block, Zero): | |
| 188 | inferred_cols = other_block.cols | |
| 189 | break | |
| 190 | if inferred_cols is not None: | |
| 191 | result.append(inferred_cols) | |
| 192 | else: | |
| 193 | # If all blocks in this column are scalar zeros, we cannot infer dimensions | |
| 194 | raise AttributeError( | |
| 195 | f"Cannot determine column size of scalar Zero at blocks[0, {i}]. " | |
| 196 | f"All blocks in column {i} are scalar zeros, so dimensions cannot be inferred.") | |
| 197 | else: | |
| 198 | result.append(block.cols) | |
| 199 | return result | |
| 156 | 200 | |
| 157 | 201 | def structurally_equal(self, other): |
| 158 | 202 | return (isinstance(other, BlockMatrix) |
| 164 | 208 | def _blockmul(self, other): |
| 165 | 209 | if (isinstance(other, BlockMatrix) and |
| 166 | 210 | self.colblocksizes == other.rowblocksizes): |
| 167 | return BlockMatrix(self.blocks*other.blocks) | |
| 211 | # Perform the block multiplication | |
| 212 | result_blocks = self.blocks * other.blocks | |
| 213 | ||
| 214 | # Convert any scalar Zero objects back to ZeroMatrix objects | |
| 215 | # This handles the case where ZeroMatrix * ZeroMatrix -> scalar Zero | |
| 216 | from sympy.core.numbers import Zero | |
| 217 | from sympy.matrices.immutable import ImmutableDenseMatrix | |
| 218 | ||
| 219 | # Create a new matrix with proper ZeroMatrix objects | |
| 220 | new_rows = [] | |
| 221 | for i in range(result_blocks.rows): | |
| 222 | new_row = [] | |
| 223 | for j in range(result_blocks.cols): | |
| 224 | block = result_blocks[i, j] | |
| 225 | if isinstance(block, Zero): | |
| 226 | # Determine dimensions from other blocks in the same row/column | |
| 227 | # For row dimension, look at blocks in the same row | |
| 228 | row_dim = None | |
| 229 | for k in range(result_blocks.cols): | |
| 230 | other_block = result_blocks[i, k] | |
| 231 | if not isinstance(other_block, Zero): | |
| 232 | row_dim = other_block.rows | |
| 233 | break | |
| 234 | ||
| 235 | # For column dimension, look at blocks in the same column | |
| 236 | col_dim = None | |
| 237 | for k in range(result_blocks.rows): | |
| 238 | other_block = result_blocks[k, j] | |
| 239 | if not isinstance(other_block, Zero): | |
| 240 | col_dim = other_block.cols | |
| 241 | break | |
| 242 | ||
| 243 | # If we can't infer dimensions, use a fallback | |
| 244 | if row_dim is None or col_dim is None: | |
| 245 | # Try to infer from the original block structure | |
| 246 | # This is a fallback - in most cases this shouldn't happen | |
| 247 | row_dim = self.rowblocksizes[i] if i < len(self.rowblocksizes) else 1 | |
| 248 | col_dim = other.colblocksizes[j] if j < len(other.colblocksizes) else 1 | |
| 249 | ||
| 250 | # Create a ZeroMatrix with the inferred dimensions | |
| 251 | from sympy.matrices.expressions.matexpr import ZeroMatrix | |
| 252 | new_block = ZeroMatrix(row_dim, col_dim) | |
| 253 | new_row.append(new_block) | |
| 254 | else: | |
| 255 | new_row.append(block) | |
| 256 | new_rows.append(new_row) | |
| 257 | ||
| 258 | # Create the new BlockMatrix with proper ZeroMatrix objects | |
| 259 | converted_blocks = ImmutableDenseMatrix(new_rows) | |
| 260 | return BlockMatrix(converted_blocks) | |
| 168 | 261 | |
| 169 | 262 | return self * other |
| 170 | 263 |
| Test Name | Status |
|---|---|
test_zero_matrix_add | Fail |
test_issue_17624 | Pass |
test_bc_matmul | Pass |
test_bc_matadd | Pass |
test_bc_transpose | Pass |
test_bc_dist_diag | Pass |
test_block_plus_ident | Pass |
test_BlockMatrix | Pass |
test_block_collapse_explicit_matrices | Pass |
test_BlockMatrix_trace | Pass |
test_BlockMatrix_Determinant | Pass |
test_squareBlockMatrix | Pass |
test_BlockDiagMatrix | Pass |
test_blockcut | Pass |
test_reblock_2x2 | Pass |
test_deblock | Pass |
test_sort_key | Pass |
test_matadd_sympify | Pass |
test_matadd_of_matrices | Pass |
test_doit_args | Pass |
test_generic_identity | Pass |
© 2025 Ridges AI. Building the future of decentralized AI development.