|
20 | 20 |
|
21 | 21 | from linopy import GREATER_EQUAL, LESS_EQUAL, Model, solvers |
22 | 22 | from linopy.common import to_path |
| 23 | +from linopy.scaling import ScaleOptions |
23 | 24 | from linopy.solvers import _new_highspy_mps_layout, available_solvers, quadratic_solvers |
24 | 25 |
|
25 | 26 | logger = logging.getLogger(__name__) |
@@ -532,6 +533,58 @@ def test_non_aligned_variables( |
532 | 533 | assert np.issubdtype(dtype, np.floating) |
533 | 534 |
|
534 | 535 |
|
| 536 | +def _build_scaling_model() -> Model: |
| 537 | + m = Model() |
| 538 | + x = m.add_variables(lower=0, name="x") |
| 539 | + y = m.add_variables(lower=0, name="y") |
| 540 | + m.add_constraints(1000 * x + 2 * y >= 10, name="c0") |
| 541 | + m.add_constraints(0.1 * x + 0.5 * y >= 1, name="c1") |
| 542 | + m.objective = 10 * x + y |
| 543 | + return m |
| 544 | + |
| 545 | + |
| 546 | +@pytest.mark.skipif("highs" not in available_solvers, reason="Highs not installed") |
| 547 | +def test_scaling_integration_row_only() -> None: |
| 548 | + base = _build_scaling_model() |
| 549 | + status, _ = base.solve("highs", io_api="direct") |
| 550 | + assert status == "ok" |
| 551 | + base_solution = base.solution.to_pandas() |
| 552 | + base_obj = base.objective.value or 0.0 |
| 553 | + |
| 554 | + scaled = _build_scaling_model() |
| 555 | + status, _ = scaled.solve("highs", io_api="direct", scale=True) |
| 556 | + assert status == "ok" |
| 557 | + scaled_solution = scaled.solution.to_pandas() |
| 558 | + scaled_obj = scaled.objective.value or 0.0 |
| 559 | + |
| 560 | + assert np.allclose(base_solution.values, scaled_solution.values) |
| 561 | + assert np.isclose(base_obj, scaled_obj) |
| 562 | + |
| 563 | + |
| 564 | +@pytest.mark.skipif("highs" not in available_solvers, reason="Highs not installed") |
| 565 | +def test_scaling_integration_row_and_column() -> None: |
| 566 | + base = _build_scaling_model() |
| 567 | + status, _ = base.solve("highs", io_api="direct") |
| 568 | + assert status == "ok" |
| 569 | + base_solution = base.solution.to_pandas() |
| 570 | + base_obj = base.objective.value or 0.0 |
| 571 | + |
| 572 | + scaled = _build_scaling_model() |
| 573 | + status, _ = scaled.solve( |
| 574 | + "highs", |
| 575 | + io_api="direct", |
| 576 | + scale=ScaleOptions( |
| 577 | + enabled=True, variable_scaling=True, scale_integer_variables=False |
| 578 | + ), |
| 579 | + ) |
| 580 | + assert status == "ok" |
| 581 | + scaled_solution = scaled.solution.to_pandas() |
| 582 | + scaled_obj = scaled.objective.value or 0.0 |
| 583 | + |
| 584 | + assert np.allclose(base_solution.values, scaled_solution.values) |
| 585 | + assert np.isclose(base_obj, scaled_obj) |
| 586 | + |
| 587 | + |
535 | 588 | @pytest.mark.parametrize("solver,io_api,explicit_coordinate_names", params) |
536 | 589 | def test_set_files( |
537 | 590 | tmp_path: Any, |
|
0 commit comments