|
66 | 66 | ) |
67 | 67 | from linopy.config import options |
68 | 68 | from linopy.constants import ( |
| 69 | + CV_DIM, |
69 | 70 | EQUAL, |
70 | 71 | FACTOR_DIM, |
71 | 72 | GREATER_EQUAL, |
@@ -1473,63 +1474,73 @@ def simplify(self) -> LinearExpression: |
1473 | 1474 | """ |
1474 | 1475 |
|
1475 | 1476 | def _simplify_row(vars_row: np.ndarray, coeffs_row: np.ndarray) -> np.ndarray: |
1476 | | - """Simplify a single row by grouping vars and summing coefficients. |
1477 | | - |
| 1477 | + """ |
| 1478 | + Simplify a single row by grouping vars and summing coefficients. |
| 1479 | +
|
1478 | 1480 | Returns a 2D array of shape (2, input_len) where first row is vars, second is coeffs. |
1479 | 1481 | """ |
1480 | 1482 | input_len = len(vars_row) |
1481 | | - |
| 1483 | + |
1482 | 1484 | # Filter out invalid entries |
1483 | 1485 | mask = (vars_row != -1) & (coeffs_row != 0) & ~np.isnan(coeffs_row) |
1484 | 1486 | valid_vars = vars_row[mask] |
1485 | 1487 | valid_coeffs = coeffs_row[mask] |
1486 | 1488 |
|
1487 | 1489 | if len(valid_vars) == 0: |
1488 | 1490 | # Return arrays filled with -1 and 0.0, same length as input |
1489 | | - return np.vstack([ |
1490 | | - np.full(input_len, -1, dtype=float), |
1491 | | - np.zeros(input_len, dtype=float) |
1492 | | - ]) |
| 1491 | + return np.vstack( |
| 1492 | + [ |
| 1493 | + np.full(input_len, -1, dtype=float), |
| 1494 | + np.zeros(input_len, dtype=float), |
| 1495 | + ] |
| 1496 | + ) |
1493 | 1497 |
|
1494 | 1498 | # Use bincount to sum coefficients for each variable ID efficiently |
1495 | 1499 | max_var = int(valid_vars.max()) |
1496 | | - summed = np.bincount(valid_vars, weights=valid_coeffs, minlength=max_var + 1) |
| 1500 | + summed = np.bincount( |
| 1501 | + valid_vars, weights=valid_coeffs, minlength=max_var + 1 |
| 1502 | + ) |
1497 | 1503 |
|
1498 | 1504 | # Get non-zero entries |
1499 | 1505 | unique_vars = np.where(summed != 0)[0] |
1500 | 1506 | unique_coeffs = summed[unique_vars] |
1501 | | - |
| 1507 | + |
1502 | 1508 | # Pad to match input length |
1503 | 1509 | result_vars = np.full(input_len, -1, dtype=float) |
1504 | 1510 | result_coeffs = np.zeros(input_len, dtype=float) |
1505 | | - |
| 1511 | + |
1506 | 1512 | n_unique = len(unique_vars) |
1507 | 1513 | result_vars[:n_unique] = unique_vars |
1508 | 1514 | result_coeffs[:n_unique] = unique_coeffs |
1509 | 1515 |
|
1510 | 1516 | return np.vstack([result_vars, result_coeffs]) |
1511 | 1517 |
|
1512 | | - # Stack vars and coeffs, apply simplification once, then unstack |
1513 | | - combined = xr.apply_ufunc( |
| 1518 | + # Coeffs and vars have dimensions (.., TERM_DIM) |
| 1519 | + # A row-wise operation is applied over the .. dimensions on both coeffs and vars, which are stacked together over a new "CV_DIM" dimension |
| 1520 | + combined: xr.DataArray = xr.apply_ufunc( |
1514 | 1521 | _simplify_row, |
1515 | 1522 | self.vars, |
1516 | 1523 | self.coeffs, |
1517 | 1524 | input_core_dims=[[TERM_DIM], [TERM_DIM]], |
1518 | | - output_core_dims=[["_field", TERM_DIM]], |
| 1525 | + output_core_dims=[[CV_DIM, TERM_DIM]], |
1519 | 1526 | vectorize=True, |
1520 | 1527 | ) |
1521 | | - |
| 1528 | + # Combined has dimensions (.., CV_DIM, TERM_DIM) |
| 1529 | + |
| 1530 | + # Drop terms where all vars are -1 (i.e., empty terms across all positions) |
| 1531 | + vars = combined.isel({CV_DIM: 0}).astype(int) |
| 1532 | + non_empty_terms = (vars != -1).any(dim=[d for d in vars.dims if d != TERM_DIM]) |
| 1533 | + combined = combined.isel({TERM_DIM: non_empty_terms}) |
| 1534 | + |
1522 | 1535 | # Extract vars and coeffs from the combined result |
1523 | | - vars_simplified = combined.isel(_field=0).astype(int) |
1524 | | - coeffs_simplified = combined.isel(_field=1) |
| 1536 | + vars = combined.isel({CV_DIM: 0}).astype(int) |
| 1537 | + coeffs = combined.isel({CV_DIM: 1}) |
1525 | 1538 |
|
1526 | 1539 | # Create new dataset with simplified data |
1527 | 1540 | new_data = self.data.copy() |
1528 | | - new_data = assign_multiindex_safe( |
1529 | | - new_data, vars=vars_simplified, coeffs=coeffs_simplified |
1530 | | - ) |
| 1541 | + new_data = assign_multiindex_safe(new_data, vars=vars, coeffs=coeffs) |
1531 | 1542 |
|
1532 | | - return LinearExpression(new_data, self.model).densify_terms() |
| 1543 | + return LinearExpression(new_data, self.model) |
1533 | 1544 |
|
1534 | 1545 | @classmethod |
1535 | 1546 | def _from_scalarexpression_list( |
|
0 commit comments