| 79 | 79 | def __new__(cls, *args, **kwargs): |
| 80 | 80 | from sympy.matrices.immutable import ImmutableDenseMatrix |
| 81 | 81 | from sympy.utilities.iterables import is_sequence |
| 82 | isMat = lambda i: getattr(i, 'is_Matrix', False) | |
| 82 | from sympy.core.numbers import Zero | |
| 83 | # Accept both Matrix objects and Zero objects (which result from matrix multiplication) | |
| 84 | isMat = lambda i: getattr(i, 'is_Matrix', False) or isinstance(i, Zero) | |
| 83 | 85 | if len(args) != 1 or \ |
| 84 | 86 | not is_sequence(args[0]) or \ |
| 85 | 87 | len(set([isMat(r) for r in args[0]])) != 1: |
| 95 | 97 | blocky = ok = len(set([len(r) for r in rows])) == 1 |
| 96 | 98 | if ok: |
| 97 | 99 | # same number of rows for each matrix in a row |
| 98 | for r in rows: | |
| 99 | ok = len(set([i.rows for i in r])) == 1 | |
| 100 | for r_idx, r in enumerate(rows): | |
| 101 | # For each row, get row sizes, handling Zero objects | |
| 102 | row_sizes = [] | |
| 103 | for i in r: | |
| 104 | if hasattr(i, 'rows'): | |
| 105 | row_sizes.append(i.rows) | |
| 106 | else: | |
| 107 | # For Zero objects, infer from other blocks in same row | |
| 108 | # This shouldn't happen in normal cases, but just in case | |
| 109 | row_sizes.append(None) # Will be validated later | |
| 110 | if None not in row_sizes: | |
| 111 | ok = len(set(row_sizes)) == 1 | |
| 112 | else: | |
| 113 | # If we have None values, we need to infer them | |
| 114 | # In a valid block matrix, all blocks in a row should have same row count | |
| 115 | # Find a non-None value to use as reference | |
| 116 | valid_size = None | |
| 117 | for size in row_sizes: | |
| 118 | if size is not None: | |
| 119 | valid_size = size | |
| 120 | break | |
| 121 | if valid_size is not None: | |
| 122 | # All should be the same | |
| 123 | ok = all(size == valid_size or size is None for size in row_sizes) | |
| 124 | else: | |
| 125 | # All are None, this is problematic | |
| 126 | ok = False | |
| 100 | 127 | if not ok: |
| 101 | 128 | break |
| 102 | 129 | blocky = ok |
| 103 | 130 | # same number of cols for each matrix in each col |
| 104 | 131 | for c in range(len(rows[0])): |
| 105 | ok = len(set([rows[i][c].cols | |
| 106 | for i in range(len(rows))])) == 1 | |
| 132 | # For each column, get column sizes, handling Zero objects | |
| 133 | col_sizes = [] | |
| 134 | for r_idx in range(len(rows)): | |
| 135 | block = rows[r_idx][c] | |
| 136 | if hasattr(block, 'cols'): | |
| 137 | col_sizes.append(block.cols) | |
| 138 | else: | |
| 139 | # For Zero objects, add None for now | |
| 140 | col_sizes.append(None) | |
| 141 | if None not in col_sizes: | |
| 142 | ok = len(set(col_sizes)) == 1 | |
| 143 | else: | |
| 144 | # If we have None values, we need to infer them | |
| 145 | # In a valid block matrix, all blocks in a column should have same col count | |
| 146 | # Find a non-None value to use as reference | |
| 147 | valid_size = None | |
| 148 | for size in col_sizes: | |
| 149 | if size is not None: | |
| 150 | valid_size = size | |
| 151 | break | |
| 152 | if valid_size is not None: | |
| 153 | # All should be the same | |
| 154 | ok = all(size == valid_size or size is None for size in col_sizes) | |
| 155 | else: | |
| 156 | # All are None, this is problematic | |
| 157 | ok = False | |
| 107 | 158 | if not ok: |
| 108 | 159 | break |
| 109 | 160 | if not ok: |
| 110 | 161 | # same total cols in each row |
| 111 | ok = len(set([ | |
| 112 | sum([i.cols for i in r]) for r in rows])) == 1 | |
| 162 | total_cols_list = [] | |
| 163 | for r in rows: | |
| 164 | row_total = 0 | |
| 165 | for i in r: | |
| 166 | if hasattr(i, 'cols'): | |
| 167 | row_total += i.cols | |
| 168 | else: | |
| 169 | # For Zero objects, we need to infer the column count | |
| 170 | # This is more complex, so for now we'll skip this validation | |
| 171 | # when Zero objects are present, assuming the structure is valid | |
| 172 | row_total = None | |
| 173 | break | |
| 174 | if row_total is not None: | |
| 175 | total_cols_list.append(row_total) | |
| 176 | else: | |
| 177 | # If we can't determine total cols due to Zero objects, | |
| 178 | # assume the structure is valid and skip this check | |
| 179 | total_cols_list = None | |
| 180 | break | |
| 181 | if total_cols_list is not None: | |
| 182 | ok = len(set(total_cols_list)) == 1 | |
| 183 | else: | |
| 184 | # Skip this validation when Zero objects prevent column counting | |
| 185 | ok = True | |
| 113 | 186 | if blocky and ok: |
| 114 | 187 | raise ValueError(filldedent(''' |
| 115 | 188 | Although this matrix is comprised of blocks, |
| 148 | 221 | |
| 149 | 222 | @property |
| 150 | 223 | def rowblocksizes(self): |
| 151 | return [self.blocks[i, 0].rows for i in range(self.blockshape[0])] | |
| 224 | sizes = [] | |
| 225 | for i in range(self.blockshape[0]): | |
| 226 | block = self.blocks[i, 0] | |
| 227 | if hasattr(block, 'rows'): | |
| 228 | sizes.append(block.rows) | |
| 229 | else: | |
| 230 | # For cases where blocks might be Zero objects, | |
| 231 | # find another block in the same row to get the row size | |
| 232 | for j in range(self.blockshape[1]): | |
| 233 | other_block = self.blocks[i, j] | |
| 234 | if hasattr(other_block, 'rows'): | |
| 235 | sizes.append(other_block.rows) | |
| 236 | break | |
| 237 | else: | |
| 238 | # If no block in this row has rows attribute, | |
| 239 | # this shouldn't happen in valid block matrices | |
| 240 | raise AttributeError("No block in row %d has rows attribute" % i) | |
| 241 | return sizes | |
| 152 | 242 | |
| 153 | 243 | @property |
| 154 | 244 | def colblocksizes(self): |
| 155 | return [self.blocks[0, i].cols for i in range(self.blockshape[1])] | |
| 245 | sizes = [] | |
| 246 | for i in range(self.blockshape[1]): | |
| 247 | block = self.blocks[0, i] | |
| 248 | if hasattr(block, 'cols'): | |
| 249 | sizes.append(block.cols) | |
| 250 | else: | |
| 251 | # For cases where blocks might be Zero objects, | |
| 252 | # find another block in the same column to get the column size | |
| 253 | for j in range(self.blockshape[0]): | |
| 254 | other_block = self.blocks[j, i] | |
| 255 | if hasattr(other_block, 'cols'): | |
| 256 | sizes.append(other_block.cols) | |
| 257 | break | |
| 258 | else: | |
| 259 | # If no block in this column has cols attribute, | |
| 260 | # this shouldn't happen in valid block matrices | |
| 261 | raise AttributeError("No block in column %d has cols attribute" % i) | |
| 262 | return sizes | |
| 156 | 263 | |
| 157 | 264 | def structurally_equal(self, other): |
| 158 | 265 | return (isinstance(other, BlockMatrix) |
| 164 | 271 | def _blockmul(self, other): |
| 165 | 272 | if (isinstance(other, BlockMatrix) and |
| 166 | 273 | self.colblocksizes == other.rowblocksizes): |
| 167 | return BlockMatrix(self.blocks*other.blocks) | |
| 274 | # Perform the block multiplication | |
| 275 | result_blocks = self.blocks * other.blocks | |
| 276 | ||
| 277 | # Convert any Zero objects back to ZeroMatrix objects with proper dimensions | |
| 278 | from sympy.core.numbers import Zero | |
| 279 | from sympy.matrices.immutable import ImmutableDenseMatrix | |
| 280 | ||
| 281 | # Create a new matrix with proper ZeroMatrix objects | |
| 282 | new_blocks_data = [] | |
| 283 | for i in range(result_blocks.rows): | |
| 284 | row_data = [] | |
| 285 | for j in range(result_blocks.cols): | |
| 286 | block = result_blocks[i, j] | |
| 287 | if isinstance(block, Zero): | |
| 288 | # Replace Zero with ZeroMatrix of appropriate dimensions | |
| 289 | # The dimensions should match the block structure | |
| 290 | rows = self.rowblocksizes[i] | |
| 291 | cols = other.colblocksizes[j] | |
| 292 | row_data.append(ZeroMatrix(rows, cols)) | |
| 293 | else: | |
| 294 | row_data.append(block) | |
| 295 | new_blocks_data.append(row_data) | |
| 296 | ||
| 297 | return BlockMatrix(new_blocks_data) | |
| 168 | 298 | |
| 169 | 299 | return self * other |
| 170 | 300 |
| 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.